##// END OF EJS Templates
pull: add `--confirm` flag to confirm before writing changes...
Pulkit Goyal -
r45033:bd7b2c8d default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,7831 +1,7833 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 .pycompat import open
25 from .pycompat import open
26 from . import (
26 from . import (
27 archival,
27 archival,
28 bookmarks,
28 bookmarks,
29 bundle2,
29 bundle2,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 merge as mergemod,
48 merge as mergemod,
49 narrowspec,
49 narrowspec,
50 obsolete,
50 obsolete,
51 obsutil,
51 obsutil,
52 patch,
52 patch,
53 phases,
53 phases,
54 pycompat,
54 pycompat,
55 rcutil,
55 rcutil,
56 registrar,
56 registrar,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 shelve as shelvemod,
61 shelve as shelvemod,
62 state as statemod,
62 state as statemod,
63 streamclone,
63 streamclone,
64 tags as tagsmod,
64 tags as tagsmod,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 verify as verifymod,
67 verify as verifymod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 )
73 )
74
74
75 table = {}
75 table = {}
76 table.update(debugcommandsmod.command._table)
76 table.update(debugcommandsmod.command._table)
77
77
78 command = registrar.command(table)
78 command = registrar.command(table)
79 INTENT_READONLY = registrar.INTENT_READONLY
79 INTENT_READONLY = registrar.INTENT_READONLY
80
80
81 # common command options
81 # common command options
82
82
83 globalopts = [
83 globalopts = [
84 (
84 (
85 b'R',
85 b'R',
86 b'repository',
86 b'repository',
87 b'',
87 b'',
88 _(b'repository root directory or name of overlay bundle file'),
88 _(b'repository root directory or name of overlay bundle file'),
89 _(b'REPO'),
89 _(b'REPO'),
90 ),
90 ),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (
92 (
93 b'y',
93 b'y',
94 b'noninteractive',
94 b'noninteractive',
95 None,
95 None,
96 _(
96 _(
97 b'do not prompt, automatically pick the first choice for all prompts'
97 b'do not prompt, automatically pick the first choice for all prompts'
98 ),
98 ),
99 ),
99 ),
100 (b'q', b'quiet', None, _(b'suppress output')),
100 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
102 (
102 (
103 b'',
103 b'',
104 b'color',
104 b'color',
105 b'',
105 b'',
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # and should not be translated
107 # and should not be translated
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b'TYPE'),
109 _(b'TYPE'),
110 ),
110 ),
111 (
111 (
112 b'',
112 b'',
113 b'config',
113 b'config',
114 [],
114 [],
115 _(b'set/override config option (use \'section.name=value\')'),
115 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'CONFIG'),
116 _(b'CONFIG'),
117 ),
117 ),
118 (b'', b'debug', None, _(b'enable debugging output')),
118 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debugger', None, _(b'start debugger')),
119 (b'', b'debugger', None, _(b'start debugger')),
120 (
120 (
121 b'',
121 b'',
122 b'encoding',
122 b'encoding',
123 encoding.encoding,
123 encoding.encoding,
124 _(b'set the charset encoding'),
124 _(b'set the charset encoding'),
125 _(b'ENCODE'),
125 _(b'ENCODE'),
126 ),
126 ),
127 (
127 (
128 b'',
128 b'',
129 b'encodingmode',
129 b'encodingmode',
130 encoding.encodingmode,
130 encoding.encodingmode,
131 _(b'set the charset encoding mode'),
131 _(b'set the charset encoding mode'),
132 _(b'MODE'),
132 _(b'MODE'),
133 ),
133 ),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'time', None, _(b'time how long the command takes')),
135 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'profile', None, _(b'print command execution profile')),
136 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'version', None, _(b'output version information and exit')),
137 (b'', b'version', None, _(b'output version information and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (
140 (
141 b'',
141 b'',
142 b'pager',
142 b'pager',
143 b'auto',
143 b'auto',
144 _(b"when to paginate (boolean, always, auto, or never)"),
144 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b'TYPE'),
145 _(b'TYPE'),
146 ),
146 ),
147 ]
147 ]
148
148
149 dryrunopts = cmdutil.dryrunopts
149 dryrunopts = cmdutil.dryrunopts
150 remoteopts = cmdutil.remoteopts
150 remoteopts = cmdutil.remoteopts
151 walkopts = cmdutil.walkopts
151 walkopts = cmdutil.walkopts
152 commitopts = cmdutil.commitopts
152 commitopts = cmdutil.commitopts
153 commitopts2 = cmdutil.commitopts2
153 commitopts2 = cmdutil.commitopts2
154 commitopts3 = cmdutil.commitopts3
154 commitopts3 = cmdutil.commitopts3
155 formatteropts = cmdutil.formatteropts
155 formatteropts = cmdutil.formatteropts
156 templateopts = cmdutil.templateopts
156 templateopts = cmdutil.templateopts
157 logopts = cmdutil.logopts
157 logopts = cmdutil.logopts
158 diffopts = cmdutil.diffopts
158 diffopts = cmdutil.diffopts
159 diffwsopts = cmdutil.diffwsopts
159 diffwsopts = cmdutil.diffwsopts
160 diffopts2 = cmdutil.diffopts2
160 diffopts2 = cmdutil.diffopts2
161 mergetoolopts = cmdutil.mergetoolopts
161 mergetoolopts = cmdutil.mergetoolopts
162 similarityopts = cmdutil.similarityopts
162 similarityopts = cmdutil.similarityopts
163 subrepoopts = cmdutil.subrepoopts
163 subrepoopts = cmdutil.subrepoopts
164 debugrevlogopts = cmdutil.debugrevlogopts
164 debugrevlogopts = cmdutil.debugrevlogopts
165
165
166 # Commands start here, listed alphabetically
166 # Commands start here, listed alphabetically
167
167
168
168
169 @command(
169 @command(
170 b'abort',
170 b'abort',
171 dryrunopts,
171 dryrunopts,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpbasic=True,
173 helpbasic=True,
174 )
174 )
175 def abort(ui, repo, **opts):
175 def abort(ui, repo, **opts):
176 """abort an unfinished operation (EXPERIMENTAL)
176 """abort an unfinished operation (EXPERIMENTAL)
177
177
178 Aborts a multistep operation like graft, histedit, rebase, merge,
178 Aborts a multistep operation like graft, histedit, rebase, merge,
179 and unshelve if they are in an unfinished state.
179 and unshelve if they are in an unfinished state.
180
180
181 use --dry-run/-n to dry run the command.
181 use --dry-run/-n to dry run the command.
182 """
182 """
183 dryrun = opts.get('dry_run')
183 dryrun = opts.get('dry_run')
184 abortstate = cmdutil.getunfinishedstate(repo)
184 abortstate = cmdutil.getunfinishedstate(repo)
185 if not abortstate:
185 if not abortstate:
186 raise error.Abort(_(b'no operation in progress'))
186 raise error.Abort(_(b'no operation in progress'))
187 if not abortstate.abortfunc:
187 if not abortstate.abortfunc:
188 raise error.Abort(
188 raise error.Abort(
189 (
189 (
190 _(b"%s in progress but does not support 'hg abort'")
190 _(b"%s in progress but does not support 'hg abort'")
191 % (abortstate._opname)
191 % (abortstate._opname)
192 ),
192 ),
193 hint=abortstate.hint(),
193 hint=abortstate.hint(),
194 )
194 )
195 if dryrun:
195 if dryrun:
196 ui.status(
196 ui.status(
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 )
198 )
199 return
199 return
200 return abortstate.abortfunc(ui, repo)
200 return abortstate.abortfunc(ui, repo)
201
201
202
202
203 @command(
203 @command(
204 b'add',
204 b'add',
205 walkopts + subrepoopts + dryrunopts,
205 walkopts + subrepoopts + dryrunopts,
206 _(b'[OPTION]... [FILE]...'),
206 _(b'[OPTION]... [FILE]...'),
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpbasic=True,
208 helpbasic=True,
209 inferrepo=True,
209 inferrepo=True,
210 )
210 )
211 def add(ui, repo, *pats, **opts):
211 def add(ui, repo, *pats, **opts):
212 """add the specified files on the next commit
212 """add the specified files on the next commit
213
213
214 Schedule files to be version controlled and added to the
214 Schedule files to be version controlled and added to the
215 repository.
215 repository.
216
216
217 The files will be added to the repository at the next commit. To
217 The files will be added to the repository at the next commit. To
218 undo an add before that, see :hg:`forget`.
218 undo an add before that, see :hg:`forget`.
219
219
220 If no names are given, add all files to the repository (except
220 If no names are given, add all files to the repository (except
221 files matching ``.hgignore``).
221 files matching ``.hgignore``).
222
222
223 .. container:: verbose
223 .. container:: verbose
224
224
225 Examples:
225 Examples:
226
226
227 - New (unknown) files are added
227 - New (unknown) files are added
228 automatically by :hg:`add`::
228 automatically by :hg:`add`::
229
229
230 $ ls
230 $ ls
231 foo.c
231 foo.c
232 $ hg status
232 $ hg status
233 ? foo.c
233 ? foo.c
234 $ hg add
234 $ hg add
235 adding foo.c
235 adding foo.c
236 $ hg status
236 $ hg status
237 A foo.c
237 A foo.c
238
238
239 - Specific files to be added can be specified::
239 - Specific files to be added can be specified::
240
240
241 $ ls
241 $ ls
242 bar.c foo.c
242 bar.c foo.c
243 $ hg status
243 $ hg status
244 ? bar.c
244 ? bar.c
245 ? foo.c
245 ? foo.c
246 $ hg add bar.c
246 $ hg add bar.c
247 $ hg status
247 $ hg status
248 A bar.c
248 A bar.c
249 ? foo.c
249 ? foo.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253
253
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 return rejected and 1 or 0
257 return rejected and 1 or 0
258
258
259
259
260 @command(
260 @command(
261 b'addremove',
261 b'addremove',
262 similarityopts + subrepoopts + walkopts + dryrunopts,
262 similarityopts + subrepoopts + walkopts + dryrunopts,
263 _(b'[OPTION]... [FILE]...'),
263 _(b'[OPTION]... [FILE]...'),
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 inferrepo=True,
265 inferrepo=True,
266 )
266 )
267 def addremove(ui, repo, *pats, **opts):
267 def addremove(ui, repo, *pats, **opts):
268 """add all new files, delete all missing files
268 """add all new files, delete all missing files
269
269
270 Add all new files and remove all missing files from the
270 Add all new files and remove all missing files from the
271 repository.
271 repository.
272
272
273 Unless names are given, new files are ignored if they match any of
273 Unless names are given, new files are ignored if they match any of
274 the patterns in ``.hgignore``. As with add, these changes take
274 the patterns in ``.hgignore``. As with add, these changes take
275 effect at the next commit.
275 effect at the next commit.
276
276
277 Use the -s/--similarity option to detect renamed files. This
277 Use the -s/--similarity option to detect renamed files. This
278 option takes a percentage between 0 (disabled) and 100 (files must
278 option takes a percentage between 0 (disabled) and 100 (files must
279 be identical) as its parameter. With a parameter greater than 0,
279 be identical) as its parameter. With a parameter greater than 0,
280 this compares every removed file with every added file and records
280 this compares every removed file with every added file and records
281 those similar enough as renames. Detecting renamed files this way
281 those similar enough as renames. Detecting renamed files this way
282 can be expensive. After using this option, :hg:`status -C` can be
282 can be expensive. After using this option, :hg:`status -C` can be
283 used to check which files were identified as moved or renamed. If
283 used to check which files were identified as moved or renamed. If
284 not specified, -s/--similarity defaults to 100 and only renames of
284 not specified, -s/--similarity defaults to 100 and only renames of
285 identical files are detected.
285 identical files are detected.
286
286
287 .. container:: verbose
287 .. container:: verbose
288
288
289 Examples:
289 Examples:
290
290
291 - A number of files (bar.c and foo.c) are new,
291 - A number of files (bar.c and foo.c) are new,
292 while foobar.c has been removed (without using :hg:`remove`)
292 while foobar.c has been removed (without using :hg:`remove`)
293 from the repository::
293 from the repository::
294
294
295 $ ls
295 $ ls
296 bar.c foo.c
296 bar.c foo.c
297 $ hg status
297 $ hg status
298 ! foobar.c
298 ! foobar.c
299 ? bar.c
299 ? bar.c
300 ? foo.c
300 ? foo.c
301 $ hg addremove
301 $ hg addremove
302 adding bar.c
302 adding bar.c
303 adding foo.c
303 adding foo.c
304 removing foobar.c
304 removing foobar.c
305 $ hg status
305 $ hg status
306 A bar.c
306 A bar.c
307 A foo.c
307 A foo.c
308 R foobar.c
308 R foobar.c
309
309
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 Afterwards, it was edited slightly::
311 Afterwards, it was edited slightly::
312
312
313 $ ls
313 $ ls
314 foo.c
314 foo.c
315 $ hg status
315 $ hg status
316 ! foobar.c
316 ! foobar.c
317 ? foo.c
317 ? foo.c
318 $ hg addremove --similarity 90
318 $ hg addremove --similarity 90
319 removing foobar.c
319 removing foobar.c
320 adding foo.c
320 adding foo.c
321 recording removal of foobar.c as rename to foo.c (94% similar)
321 recording removal of foobar.c as rename to foo.c (94% similar)
322 $ hg status -C
322 $ hg status -C
323 A foo.c
323 A foo.c
324 foobar.c
324 foobar.c
325 R foobar.c
325 R foobar.c
326
326
327 Returns 0 if all files are successfully added.
327 Returns 0 if all files are successfully added.
328 """
328 """
329 opts = pycompat.byteskwargs(opts)
329 opts = pycompat.byteskwargs(opts)
330 if not opts.get(b'similarity'):
330 if not opts.get(b'similarity'):
331 opts[b'similarity'] = b'100'
331 opts[b'similarity'] = b'100'
332 matcher = scmutil.match(repo[None], pats, opts)
332 matcher = scmutil.match(repo[None], pats, opts)
333 relative = scmutil.anypats(pats, opts)
333 relative = scmutil.anypats(pats, opts)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336
336
337
337
338 @command(
338 @command(
339 b'annotate|blame',
339 b'annotate|blame',
340 [
340 [
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (
342 (
343 b'',
343 b'',
344 b'follow',
344 b'follow',
345 None,
345 None,
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 ),
347 ),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'a', b'text', None, _(b'treat all files as text')),
349 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'f', b'file', None, _(b'list the filename')),
351 (b'f', b'file', None, _(b'list the filename')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
355 (
355 (
356 b'l',
356 b'l',
357 b'line-number',
357 b'line-number',
358 None,
358 None,
359 _(b'show line number at the first appearance'),
359 _(b'show line number at the first appearance'),
360 ),
360 ),
361 (
361 (
362 b'',
362 b'',
363 b'skip',
363 b'skip',
364 [],
364 [],
365 _(b'revset to not display (EXPERIMENTAL)'),
365 _(b'revset to not display (EXPERIMENTAL)'),
366 _(b'REV'),
366 _(b'REV'),
367 ),
367 ),
368 ]
368 ]
369 + diffwsopts
369 + diffwsopts
370 + walkopts
370 + walkopts
371 + formatteropts,
371 + formatteropts,
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpbasic=True,
374 helpbasic=True,
375 inferrepo=True,
375 inferrepo=True,
376 )
376 )
377 def annotate(ui, repo, *pats, **opts):
377 def annotate(ui, repo, *pats, **opts):
378 """show changeset information by line for each file
378 """show changeset information by line for each file
379
379
380 List changes in files, showing the revision id responsible for
380 List changes in files, showing the revision id responsible for
381 each line.
381 each line.
382
382
383 This command is useful for discovering when a change was made and
383 This command is useful for discovering when a change was made and
384 by whom.
384 by whom.
385
385
386 If you include --file, --user, or --date, the revision number is
386 If you include --file, --user, or --date, the revision number is
387 suppressed unless you also include --number.
387 suppressed unless you also include --number.
388
388
389 Without the -a/--text option, annotate will avoid processing files
389 Without the -a/--text option, annotate will avoid processing files
390 it detects as binary. With -a, annotate will annotate the file
390 it detects as binary. With -a, annotate will annotate the file
391 anyway, although the results will probably be neither useful
391 anyway, although the results will probably be neither useful
392 nor desirable.
392 nor desirable.
393
393
394 .. container:: verbose
394 .. container:: verbose
395
395
396 Template:
396 Template:
397
397
398 The following keywords are supported in addition to the common template
398 The following keywords are supported in addition to the common template
399 keywords and functions. See also :hg:`help templates`.
399 keywords and functions. See also :hg:`help templates`.
400
400
401 :lines: List of lines with annotation data.
401 :lines: List of lines with annotation data.
402 :path: String. Repository-absolute path of the specified file.
402 :path: String. Repository-absolute path of the specified file.
403
403
404 And each entry of ``{lines}`` provides the following sub-keywords in
404 And each entry of ``{lines}`` provides the following sub-keywords in
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406
406
407 :line: String. Line content.
407 :line: String. Line content.
408 :lineno: Integer. Line number at that revision.
408 :lineno: Integer. Line number at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
410
410
411 See :hg:`help templates.operators` for the list expansion syntax.
411 See :hg:`help templates.operators` for the list expansion syntax.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 """
414 """
415 opts = pycompat.byteskwargs(opts)
415 opts = pycompat.byteskwargs(opts)
416 if not pats:
416 if not pats:
417 raise error.Abort(_(b'at least one filename or pattern is required'))
417 raise error.Abort(_(b'at least one filename or pattern is required'))
418
418
419 if opts.get(b'follow'):
419 if opts.get(b'follow'):
420 # --follow is deprecated and now just an alias for -f/--file
420 # --follow is deprecated and now just an alias for -f/--file
421 # to mimic the behavior of Mercurial before version 1.5
421 # to mimic the behavior of Mercurial before version 1.5
422 opts[b'file'] = True
422 opts[b'file'] = True
423
423
424 if (
424 if (
425 not opts.get(b'user')
425 not opts.get(b'user')
426 and not opts.get(b'changeset')
426 and not opts.get(b'changeset')
427 and not opts.get(b'date')
427 and not opts.get(b'date')
428 and not opts.get(b'file')
428 and not opts.get(b'file')
429 ):
429 ):
430 opts[b'number'] = True
430 opts[b'number'] = True
431
431
432 linenumber = opts.get(b'line_number') is not None
432 linenumber = opts.get(b'line_number') is not None
433 if (
433 if (
434 linenumber
434 linenumber
435 and (not opts.get(b'changeset'))
435 and (not opts.get(b'changeset'))
436 and (not opts.get(b'number'))
436 and (not opts.get(b'number'))
437 ):
437 ):
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439
439
440 rev = opts.get(b'rev')
440 rev = opts.get(b'rev')
441 if rev:
441 if rev:
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443 ctx = scmutil.revsingle(repo, rev)
443 ctx = scmutil.revsingle(repo, rev)
444
444
445 ui.pager(b'annotate')
445 ui.pager(b'annotate')
446 rootfm = ui.formatter(b'annotate', opts)
446 rootfm = ui.formatter(b'annotate', opts)
447 if ui.debugflag:
447 if ui.debugflag:
448 shorthex = pycompat.identity
448 shorthex = pycompat.identity
449 else:
449 else:
450
450
451 def shorthex(h):
451 def shorthex(h):
452 return h[:12]
452 return h[:12]
453
453
454 if ui.quiet:
454 if ui.quiet:
455 datefunc = dateutil.shortdate
455 datefunc = dateutil.shortdate
456 else:
456 else:
457 datefunc = dateutil.datestr
457 datefunc = dateutil.datestr
458 if ctx.rev() is None:
458 if ctx.rev() is None:
459 if opts.get(b'changeset'):
459 if opts.get(b'changeset'):
460 # omit "+" suffix which is appended to node hex
460 # omit "+" suffix which is appended to node hex
461 def formatrev(rev):
461 def formatrev(rev):
462 if rev == wdirrev:
462 if rev == wdirrev:
463 return b'%d' % ctx.p1().rev()
463 return b'%d' % ctx.p1().rev()
464 else:
464 else:
465 return b'%d' % rev
465 return b'%d' % rev
466
466
467 else:
467 else:
468
468
469 def formatrev(rev):
469 def formatrev(rev):
470 if rev == wdirrev:
470 if rev == wdirrev:
471 return b'%d+' % ctx.p1().rev()
471 return b'%d+' % ctx.p1().rev()
472 else:
472 else:
473 return b'%d ' % rev
473 return b'%d ' % rev
474
474
475 def formathex(h):
475 def formathex(h):
476 if h == wdirhex:
476 if h == wdirhex:
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
478 else:
478 else:
479 return b'%s ' % shorthex(h)
479 return b'%s ' % shorthex(h)
480
480
481 else:
481 else:
482 formatrev = b'%d'.__mod__
482 formatrev = b'%d'.__mod__
483 formathex = shorthex
483 formathex = shorthex
484
484
485 opmap = [
485 opmap = [
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 ]
492 ]
493 opnamemap = {
493 opnamemap = {
494 b'rev': b'number',
494 b'rev': b'number',
495 b'node': b'changeset',
495 b'node': b'changeset',
496 b'path': b'file',
496 b'path': b'file',
497 b'lineno': b'line_number',
497 b'lineno': b'line_number',
498 }
498 }
499
499
500 if rootfm.isplain():
500 if rootfm.isplain():
501
501
502 def makefunc(get, fmt):
502 def makefunc(get, fmt):
503 return lambda x: fmt(get(x))
503 return lambda x: fmt(get(x))
504
504
505 else:
505 else:
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return get
508 return get
509
509
510 datahint = rootfm.datahint()
510 datahint = rootfm.datahint()
511 funcmap = [
511 funcmap = [
512 (makefunc(get, fmt), sep)
512 (makefunc(get, fmt), sep)
513 for fn, sep, get, fmt in opmap
513 for fn, sep, get, fmt in opmap
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
515 ]
515 ]
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
517 fields = b' '.join(
517 fields = b' '.join(
518 fn
518 fn
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 )
521 )
522
522
523 def bad(x, y):
523 def bad(x, y):
524 raise error.Abort(b"%s: %s" % (x, y))
524 raise error.Abort(b"%s: %s" % (x, y))
525
525
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
527
527
528 follow = not opts.get(b'no_follow')
528 follow = not opts.get(b'no_follow')
529 diffopts = patch.difffeatureopts(
529 diffopts = patch.difffeatureopts(
530 ui, opts, section=b'annotate', whitespace=True
530 ui, opts, section=b'annotate', whitespace=True
531 )
531 )
532 skiprevs = opts.get(b'skip')
532 skiprevs = opts.get(b'skip')
533 if skiprevs:
533 if skiprevs:
534 skiprevs = scmutil.revrange(repo, skiprevs)
534 skiprevs = scmutil.revrange(repo, skiprevs)
535
535
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
537 for abs in ctx.walk(m):
537 for abs in ctx.walk(m):
538 fctx = ctx[abs]
538 fctx = ctx[abs]
539 rootfm.startitem()
539 rootfm.startitem()
540 rootfm.data(path=abs)
540 rootfm.data(path=abs)
541 if not opts.get(b'text') and fctx.isbinary():
541 if not opts.get(b'text') and fctx.isbinary():
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 continue
543 continue
544
544
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 lines = fctx.annotate(
546 lines = fctx.annotate(
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
548 )
548 )
549 if not lines:
549 if not lines:
550 fm.end()
550 fm.end()
551 continue
551 continue
552 formats = []
552 formats = []
553 pieces = []
553 pieces = []
554
554
555 for f, sep in funcmap:
555 for f, sep in funcmap:
556 l = [f(n) for n in lines]
556 l = [f(n) for n in lines]
557 if fm.isplain():
557 if fm.isplain():
558 sizes = [encoding.colwidth(x) for x in l]
558 sizes = [encoding.colwidth(x) for x in l]
559 ml = max(sizes)
559 ml = max(sizes)
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561 else:
561 else:
562 formats.append([b'%s'] * len(l))
562 formats.append([b'%s'] * len(l))
563 pieces.append(l)
563 pieces.append(l)
564
564
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566 fm.startitem()
566 fm.startitem()
567 fm.context(fctx=n.fctx)
567 fm.context(fctx=n.fctx)
568 fm.write(fields, b"".join(f), *p)
568 fm.write(fields, b"".join(f), *p)
569 if n.skip:
569 if n.skip:
570 fmt = b"* %s"
570 fmt = b"* %s"
571 else:
571 else:
572 fmt = b": %s"
572 fmt = b": %s"
573 fm.write(b'line', fmt, n.text)
573 fm.write(b'line', fmt, n.text)
574
574
575 if not lines[-1].text.endswith(b'\n'):
575 if not lines[-1].text.endswith(b'\n'):
576 fm.plain(b'\n')
576 fm.plain(b'\n')
577 fm.end()
577 fm.end()
578
578
579 rootfm.end()
579 rootfm.end()
580
580
581
581
582 @command(
582 @command(
583 b'archive',
583 b'archive',
584 [
584 [
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586 (
586 (
587 b'p',
587 b'p',
588 b'prefix',
588 b'prefix',
589 b'',
589 b'',
590 _(b'directory prefix for files in archive'),
590 _(b'directory prefix for files in archive'),
591 _(b'PREFIX'),
591 _(b'PREFIX'),
592 ),
592 ),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 ]
595 ]
596 + subrepoopts
596 + subrepoopts
597 + walkopts,
597 + walkopts,
598 _(b'[OPTION]... DEST'),
598 _(b'[OPTION]... DEST'),
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
600 )
600 )
601 def archive(ui, repo, dest, **opts):
601 def archive(ui, repo, dest, **opts):
602 '''create an unversioned archive of a repository revision
602 '''create an unversioned archive of a repository revision
603
603
604 By default, the revision used is the parent of the working
604 By default, the revision used is the parent of the working
605 directory; use -r/--rev to specify a different revision.
605 directory; use -r/--rev to specify a different revision.
606
606
607 The archive type is automatically detected based on file
607 The archive type is automatically detected based on file
608 extension (to override, use -t/--type).
608 extension (to override, use -t/--type).
609
609
610 .. container:: verbose
610 .. container:: verbose
611
611
612 Examples:
612 Examples:
613
613
614 - create a zip file containing the 1.0 release::
614 - create a zip file containing the 1.0 release::
615
615
616 hg archive -r 1.0 project-1.0.zip
616 hg archive -r 1.0 project-1.0.zip
617
617
618 - create a tarball excluding .hg files::
618 - create a tarball excluding .hg files::
619
619
620 hg archive project.tar.gz -X ".hg*"
620 hg archive project.tar.gz -X ".hg*"
621
621
622 Valid types are:
622 Valid types are:
623
623
624 :``files``: a directory full of files (default)
624 :``files``: a directory full of files (default)
625 :``tar``: tar archive, uncompressed
625 :``tar``: tar archive, uncompressed
626 :``tbz2``: tar archive, compressed using bzip2
626 :``tbz2``: tar archive, compressed using bzip2
627 :``tgz``: tar archive, compressed using gzip
627 :``tgz``: tar archive, compressed using gzip
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
629 :``uzip``: zip archive, uncompressed
629 :``uzip``: zip archive, uncompressed
630 :``zip``: zip archive, compressed using deflate
630 :``zip``: zip archive, compressed using deflate
631
631
632 The exact name of the destination archive or directory is given
632 The exact name of the destination archive or directory is given
633 using a format string; see :hg:`help export` for details.
633 using a format string; see :hg:`help export` for details.
634
634
635 Each member added to an archive file has a directory prefix
635 Each member added to an archive file has a directory prefix
636 prepended. Use -p/--prefix to specify a format string for the
636 prepended. Use -p/--prefix to specify a format string for the
637 prefix. The default is the basename of the archive, with suffixes
637 prefix. The default is the basename of the archive, with suffixes
638 removed.
638 removed.
639
639
640 Returns 0 on success.
640 Returns 0 on success.
641 '''
641 '''
642
642
643 opts = pycompat.byteskwargs(opts)
643 opts = pycompat.byteskwargs(opts)
644 rev = opts.get(b'rev')
644 rev = opts.get(b'rev')
645 if rev:
645 if rev:
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647 ctx = scmutil.revsingle(repo, rev)
647 ctx = scmutil.revsingle(repo, rev)
648 if not ctx:
648 if not ctx:
649 raise error.Abort(_(b'no working directory: please specify a revision'))
649 raise error.Abort(_(b'no working directory: please specify a revision'))
650 node = ctx.node()
650 node = ctx.node()
651 dest = cmdutil.makefilename(ctx, dest)
651 dest = cmdutil.makefilename(ctx, dest)
652 if os.path.realpath(dest) == repo.root:
652 if os.path.realpath(dest) == repo.root:
653 raise error.Abort(_(b'repository root cannot be destination'))
653 raise error.Abort(_(b'repository root cannot be destination'))
654
654
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
656 prefix = opts.get(b'prefix')
656 prefix = opts.get(b'prefix')
657
657
658 if dest == b'-':
658 if dest == b'-':
659 if kind == b'files':
659 if kind == b'files':
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
661 dest = cmdutil.makefileobj(ctx, dest)
661 dest = cmdutil.makefileobj(ctx, dest)
662 if not prefix:
662 if not prefix:
663 prefix = os.path.basename(repo.root) + b'-%h'
663 prefix = os.path.basename(repo.root) + b'-%h'
664
664
665 prefix = cmdutil.makefilename(ctx, prefix)
665 prefix = cmdutil.makefilename(ctx, prefix)
666 match = scmutil.match(ctx, [], opts)
666 match = scmutil.match(ctx, [], opts)
667 archival.archive(
667 archival.archive(
668 repo,
668 repo,
669 dest,
669 dest,
670 node,
670 node,
671 kind,
671 kind,
672 not opts.get(b'no_decode'),
672 not opts.get(b'no_decode'),
673 match,
673 match,
674 prefix,
674 prefix,
675 subrepos=opts.get(b'subrepos'),
675 subrepos=opts.get(b'subrepos'),
676 )
676 )
677
677
678
678
679 @command(
679 @command(
680 b'backout',
680 b'backout',
681 [
681 [
682 (
682 (
683 b'',
683 b'',
684 b'merge',
684 b'merge',
685 None,
685 None,
686 _(b'merge with old dirstate parent after backout'),
686 _(b'merge with old dirstate parent after backout'),
687 ),
687 ),
688 (
688 (
689 b'',
689 b'',
690 b'commit',
690 b'commit',
691 None,
691 None,
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
693 ),
693 ),
694 (b'', b'no-commit', None, _(b'do not commit')),
694 (b'', b'no-commit', None, _(b'do not commit')),
695 (
695 (
696 b'',
696 b'',
697 b'parent',
697 b'parent',
698 b'',
698 b'',
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
700 _(b'REV'),
700 _(b'REV'),
701 ),
701 ),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 ]
704 ]
705 + mergetoolopts
705 + mergetoolopts
706 + walkopts
706 + walkopts
707 + commitopts
707 + commitopts
708 + commitopts2,
708 + commitopts2,
709 _(b'[OPTION]... [-r] REV'),
709 _(b'[OPTION]... [-r] REV'),
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
711 )
711 )
712 def backout(ui, repo, node=None, rev=None, **opts):
712 def backout(ui, repo, node=None, rev=None, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Prepare a new changeset with the effect of REV undone in the
715 Prepare a new changeset with the effect of REV undone in the
716 current working directory. If no conflicts were encountered,
716 current working directory. If no conflicts were encountered,
717 it will be committed immediately.
717 it will be committed immediately.
718
718
719 If REV is the parent of the working directory, then this new changeset
719 If REV is the parent of the working directory, then this new changeset
720 is committed automatically (unless --no-commit is specified).
720 is committed automatically (unless --no-commit is specified).
721
721
722 .. note::
722 .. note::
723
723
724 :hg:`backout` cannot be used to fix either an unwanted or
724 :hg:`backout` cannot be used to fix either an unwanted or
725 incorrect merge.
725 incorrect merge.
726
726
727 .. container:: verbose
727 .. container:: verbose
728
728
729 Examples:
729 Examples:
730
730
731 - Reverse the effect of the parent of the working directory.
731 - Reverse the effect of the parent of the working directory.
732 This backout will be committed immediately::
732 This backout will be committed immediately::
733
733
734 hg backout -r .
734 hg backout -r .
735
735
736 - Reverse the effect of previous bad revision 23::
736 - Reverse the effect of previous bad revision 23::
737
737
738 hg backout -r 23
738 hg backout -r 23
739
739
740 - Reverse the effect of previous bad revision 23 and
740 - Reverse the effect of previous bad revision 23 and
741 leave changes uncommitted::
741 leave changes uncommitted::
742
742
743 hg backout -r 23 --no-commit
743 hg backout -r 23 --no-commit
744 hg commit -m "Backout revision 23"
744 hg commit -m "Backout revision 23"
745
745
746 By default, the pending changeset will have one parent,
746 By default, the pending changeset will have one parent,
747 maintaining a linear history. With --merge, the pending
747 maintaining a linear history. With --merge, the pending
748 changeset will instead have two parents: the old parent of the
748 changeset will instead have two parents: the old parent of the
749 working directory and a new child of REV that simply undoes REV.
749 working directory and a new child of REV that simply undoes REV.
750
750
751 Before version 1.7, the behavior without --merge was equivalent
751 Before version 1.7, the behavior without --merge was equivalent
752 to specifying --merge followed by :hg:`update --clean .` to
752 to specifying --merge followed by :hg:`update --clean .` to
753 cancel the merge and leave the child of REV as a head to be
753 cancel the merge and leave the child of REV as a head to be
754 merged separately.
754 merged separately.
755
755
756 See :hg:`help dates` for a list of formats valid for -d/--date.
756 See :hg:`help dates` for a list of formats valid for -d/--date.
757
757
758 See :hg:`help revert` for a way to restore files to the state
758 See :hg:`help revert` for a way to restore files to the state
759 of another revision.
759 of another revision.
760
760
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
762 files.
762 files.
763 '''
763 '''
764 with repo.wlock(), repo.lock():
764 with repo.wlock(), repo.lock():
765 return _dobackout(ui, repo, node, rev, **opts)
765 return _dobackout(ui, repo, node, rev, **opts)
766
766
767
767
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
769 opts = pycompat.byteskwargs(opts)
769 opts = pycompat.byteskwargs(opts)
770 if opts.get(b'commit') and opts.get(b'no_commit'):
770 if opts.get(b'commit') and opts.get(b'no_commit'):
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
772 if opts.get(b'merge') and opts.get(b'no_commit'):
772 if opts.get(b'merge') and opts.get(b'no_commit'):
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
774
774
775 if rev and node:
775 if rev and node:
776 raise error.Abort(_(b"please specify just one revision"))
776 raise error.Abort(_(b"please specify just one revision"))
777
777
778 if not rev:
778 if not rev:
779 rev = node
779 rev = node
780
780
781 if not rev:
781 if not rev:
782 raise error.Abort(_(b"please specify a revision to backout"))
782 raise error.Abort(_(b"please specify a revision to backout"))
783
783
784 date = opts.get(b'date')
784 date = opts.get(b'date')
785 if date:
785 if date:
786 opts[b'date'] = dateutil.parsedate(date)
786 opts[b'date'] = dateutil.parsedate(date)
787
787
788 cmdutil.checkunfinished(repo)
788 cmdutil.checkunfinished(repo)
789 cmdutil.bailifchanged(repo)
789 cmdutil.bailifchanged(repo)
790 node = scmutil.revsingle(repo, rev).node()
790 node = scmutil.revsingle(repo, rev).node()
791
791
792 op1, op2 = repo.dirstate.parents()
792 op1, op2 = repo.dirstate.parents()
793 if not repo.changelog.isancestor(node, op1):
793 if not repo.changelog.isancestor(node, op1):
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
795
795
796 p1, p2 = repo.changelog.parents(node)
796 p1, p2 = repo.changelog.parents(node)
797 if p1 == nullid:
797 if p1 == nullid:
798 raise error.Abort(_(b'cannot backout a change with no parents'))
798 raise error.Abort(_(b'cannot backout a change with no parents'))
799 if p2 != nullid:
799 if p2 != nullid:
800 if not opts.get(b'parent'):
800 if not opts.get(b'parent'):
801 raise error.Abort(_(b'cannot backout a merge changeset'))
801 raise error.Abort(_(b'cannot backout a merge changeset'))
802 p = repo.lookup(opts[b'parent'])
802 p = repo.lookup(opts[b'parent'])
803 if p not in (p1, p2):
803 if p not in (p1, p2):
804 raise error.Abort(
804 raise error.Abort(
805 _(b'%s is not a parent of %s') % (short(p), short(node))
805 _(b'%s is not a parent of %s') % (short(p), short(node))
806 )
806 )
807 parent = p
807 parent = p
808 else:
808 else:
809 if opts.get(b'parent'):
809 if opts.get(b'parent'):
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
811 parent = p1
811 parent = p1
812
812
813 # the backout should appear on the same branch
813 # the backout should appear on the same branch
814 branch = repo.dirstate.branch()
814 branch = repo.dirstate.branch()
815 bheads = repo.branchheads(branch)
815 bheads = repo.branchheads(branch)
816 rctx = scmutil.revsingle(repo, hex(parent))
816 rctx = scmutil.revsingle(repo, hex(parent))
817 if not opts.get(b'merge') and op1 != node:
817 if not opts.get(b'merge') and op1 != node:
818 with dirstateguard.dirstateguard(repo, b'backout'):
818 with dirstateguard.dirstateguard(repo, b'backout'):
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
820 with ui.configoverride(overrides, b'backout'):
820 with ui.configoverride(overrides, b'backout'):
821 stats = mergemod.update(
821 stats = mergemod.update(
822 repo,
822 repo,
823 parent,
823 parent,
824 branchmerge=True,
824 branchmerge=True,
825 force=True,
825 force=True,
826 ancestor=node,
826 ancestor=node,
827 mergeancestor=False,
827 mergeancestor=False,
828 )
828 )
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
860 if not newnode:
860 if not newnode:
861 ui.status(_(b"nothing changed\n"))
861 ui.status(_(b"nothing changed\n"))
862 return 1
862 return 1
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
864
864
865 def nice(node):
865 def nice(node):
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
867
867
868 ui.status(
868 ui.status(
869 _(b'changeset %s backs out changeset %s\n')
869 _(b'changeset %s backs out changeset %s\n')
870 % (nice(repo.changelog.tip()), nice(node))
870 % (nice(repo.changelog.tip()), nice(node))
871 )
871 )
872 if opts.get(b'merge') and op1 != node:
872 if opts.get(b'merge') and op1 != node:
873 hg.clean(repo, op1, show_stats=False)
873 hg.clean(repo, op1, show_stats=False)
874 ui.status(
874 ui.status(
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
876 )
876 )
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 with ui.configoverride(overrides, b'backout'):
878 with ui.configoverride(overrides, b'backout'):
879 return hg.merge(repo[b'tip'])
879 return hg.merge(repo[b'tip'])
880 return 0
880 return 0
881
881
882
882
883 @command(
883 @command(
884 b'bisect',
884 b'bisect',
885 [
885 [
886 (b'r', b'reset', False, _(b'reset bisect state')),
886 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'g', b'good', False, _(b'mark changeset good')),
887 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (
891 (
892 b'c',
892 b'c',
893 b'command',
893 b'command',
894 b'',
894 b'',
895 _(b'use command to check changeset state'),
895 _(b'use command to check changeset state'),
896 _(b'CMD'),
896 _(b'CMD'),
897 ),
897 ),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
899 ],
899 ],
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 )
902 )
903 def bisect(
903 def bisect(
904 ui,
904 ui,
905 repo,
905 repo,
906 rev=None,
906 rev=None,
907 extra=None,
907 extra=None,
908 command=None,
908 command=None,
909 reset=None,
909 reset=None,
910 good=None,
910 good=None,
911 bad=None,
911 bad=None,
912 skip=None,
912 skip=None,
913 extend=None,
913 extend=None,
914 noupdate=None,
914 noupdate=None,
915 ):
915 ):
916 """subdivision search of changesets
916 """subdivision search of changesets
917
917
918 This command helps to find changesets which introduce problems. To
918 This command helps to find changesets which introduce problems. To
919 use, mark the earliest changeset you know exhibits the problem as
919 use, mark the earliest changeset you know exhibits the problem as
920 bad, then mark the latest changeset which is free from the problem
920 bad, then mark the latest changeset which is free from the problem
921 as good. Bisect will update your working directory to a revision
921 as good. Bisect will update your working directory to a revision
922 for testing (unless the -U/--noupdate option is specified). Once
922 for testing (unless the -U/--noupdate option is specified). Once
923 you have performed tests, mark the working directory as good or
923 you have performed tests, mark the working directory as good or
924 bad, and bisect will either update to another candidate changeset
924 bad, and bisect will either update to another candidate changeset
925 or announce that it has found the bad revision.
925 or announce that it has found the bad revision.
926
926
927 As a shortcut, you can also use the revision argument to mark a
927 As a shortcut, you can also use the revision argument to mark a
928 revision as good or bad without checking it out first.
928 revision as good or bad without checking it out first.
929
929
930 If you supply a command, it will be used for automatic bisection.
930 If you supply a command, it will be used for automatic bisection.
931 The environment variable HG_NODE will contain the ID of the
931 The environment variable HG_NODE will contain the ID of the
932 changeset being tested. The exit status of the command will be
932 changeset being tested. The exit status of the command will be
933 used to mark revisions as good or bad: status 0 means good, 125
933 used to mark revisions as good or bad: status 0 means good, 125
934 means to skip the revision, 127 (command not found) will abort the
934 means to skip the revision, 127 (command not found) will abort the
935 bisection, and any other non-zero exit status means the revision
935 bisection, and any other non-zero exit status means the revision
936 is bad.
936 is bad.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Some examples:
940 Some examples:
941
941
942 - start a bisection with known bad revision 34, and good revision 12::
942 - start a bisection with known bad revision 34, and good revision 12::
943
943
944 hg bisect --bad 34
944 hg bisect --bad 34
945 hg bisect --good 12
945 hg bisect --good 12
946
946
947 - advance the current bisection by marking current revision as good or
947 - advance the current bisection by marking current revision as good or
948 bad::
948 bad::
949
949
950 hg bisect --good
950 hg bisect --good
951 hg bisect --bad
951 hg bisect --bad
952
952
953 - mark the current revision, or a known revision, to be skipped (e.g. if
953 - mark the current revision, or a known revision, to be skipped (e.g. if
954 that revision is not usable because of another issue)::
954 that revision is not usable because of another issue)::
955
955
956 hg bisect --skip
956 hg bisect --skip
957 hg bisect --skip 23
957 hg bisect --skip 23
958
958
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960
960
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962
962
963 - forget the current bisection::
963 - forget the current bisection::
964
964
965 hg bisect --reset
965 hg bisect --reset
966
966
967 - use 'make && make tests' to automatically find the first broken
967 - use 'make && make tests' to automatically find the first broken
968 revision::
968 revision::
969
969
970 hg bisect --reset
970 hg bisect --reset
971 hg bisect --bad 34
971 hg bisect --bad 34
972 hg bisect --good 12
972 hg bisect --good 12
973 hg bisect --command "make && make tests"
973 hg bisect --command "make && make tests"
974
974
975 - see all changesets whose states are already known in the current
975 - see all changesets whose states are already known in the current
976 bisection::
976 bisection::
977
977
978 hg log -r "bisect(pruned)"
978 hg log -r "bisect(pruned)"
979
979
980 - see the changeset currently being bisected (especially useful
980 - see the changeset currently being bisected (especially useful
981 if running with -U/--noupdate)::
981 if running with -U/--noupdate)::
982
982
983 hg log -r "bisect(current)"
983 hg log -r "bisect(current)"
984
984
985 - see all changesets that took part in the current bisection::
985 - see all changesets that took part in the current bisection::
986
986
987 hg log -r "bisect(range)"
987 hg log -r "bisect(range)"
988
988
989 - you can even get a nice graph::
989 - you can even get a nice graph::
990
990
991 hg log --graph -r "bisect(range)"
991 hg log --graph -r "bisect(range)"
992
992
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994
994
995 Returns 0 on success.
995 Returns 0 on success.
996 """
996 """
997 # backward compatibility
997 # backward compatibility
998 if rev in b"good bad reset init".split():
998 if rev in b"good bad reset init".split():
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1000 cmd, rev, extra = rev, extra, None
1000 cmd, rev, extra = rev, extra, None
1001 if cmd == b"good":
1001 if cmd == b"good":
1002 good = True
1002 good = True
1003 elif cmd == b"bad":
1003 elif cmd == b"bad":
1004 bad = True
1004 bad = True
1005 else:
1005 else:
1006 reset = True
1006 reset = True
1007 elif extra:
1007 elif extra:
1008 raise error.Abort(_(b'incompatible arguments'))
1008 raise error.Abort(_(b'incompatible arguments'))
1009
1009
1010 incompatibles = {
1010 incompatibles = {
1011 b'--bad': bad,
1011 b'--bad': bad,
1012 b'--command': bool(command),
1012 b'--command': bool(command),
1013 b'--extend': extend,
1013 b'--extend': extend,
1014 b'--good': good,
1014 b'--good': good,
1015 b'--reset': reset,
1015 b'--reset': reset,
1016 b'--skip': skip,
1016 b'--skip': skip,
1017 }
1017 }
1018
1018
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1020
1020
1021 if len(enabled) > 1:
1021 if len(enabled) > 1:
1022 raise error.Abort(
1022 raise error.Abort(
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1024 )
1024 )
1025
1025
1026 if reset:
1026 if reset:
1027 hbisect.resetstate(repo)
1027 hbisect.resetstate(repo)
1028 return
1028 return
1029
1029
1030 state = hbisect.load_state(repo)
1030 state = hbisect.load_state(repo)
1031
1031
1032 # update state
1032 # update state
1033 if good or bad or skip:
1033 if good or bad or skip:
1034 if rev:
1034 if rev:
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1036 else:
1036 else:
1037 nodes = [repo.lookup(b'.')]
1037 nodes = [repo.lookup(b'.')]
1038 if good:
1038 if good:
1039 state[b'good'] += nodes
1039 state[b'good'] += nodes
1040 elif bad:
1040 elif bad:
1041 state[b'bad'] += nodes
1041 state[b'bad'] += nodes
1042 elif skip:
1042 elif skip:
1043 state[b'skip'] += nodes
1043 state[b'skip'] += nodes
1044 hbisect.save_state(repo, state)
1044 hbisect.save_state(repo, state)
1045 if not (state[b'good'] and state[b'bad']):
1045 if not (state[b'good'] and state[b'bad']):
1046 return
1046 return
1047
1047
1048 def mayupdate(repo, node, show_stats=True):
1048 def mayupdate(repo, node, show_stats=True):
1049 """common used update sequence"""
1049 """common used update sequence"""
1050 if noupdate:
1050 if noupdate:
1051 return
1051 return
1052 cmdutil.checkunfinished(repo)
1052 cmdutil.checkunfinished(repo)
1053 cmdutil.bailifchanged(repo)
1053 cmdutil.bailifchanged(repo)
1054 return hg.clean(repo, node, show_stats=show_stats)
1054 return hg.clean(repo, node, show_stats=show_stats)
1055
1055
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1057
1057
1058 if command:
1058 if command:
1059 changesets = 1
1059 changesets = 1
1060 if noupdate:
1060 if noupdate:
1061 try:
1061 try:
1062 node = state[b'current'][0]
1062 node = state[b'current'][0]
1063 except LookupError:
1063 except LookupError:
1064 raise error.Abort(
1064 raise error.Abort(
1065 _(
1065 _(
1066 b'current bisect revision is unknown - '
1066 b'current bisect revision is unknown - '
1067 b'start a new bisect to fix'
1067 b'start a new bisect to fix'
1068 )
1068 )
1069 )
1069 )
1070 else:
1070 else:
1071 node, p2 = repo.dirstate.parents()
1071 node, p2 = repo.dirstate.parents()
1072 if p2 != nullid:
1072 if p2 != nullid:
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1074 if rev:
1074 if rev:
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1076 with hbisect.restore_state(repo, state, node):
1076 with hbisect.restore_state(repo, state, node):
1077 while changesets:
1077 while changesets:
1078 # update state
1078 # update state
1079 state[b'current'] = [node]
1079 state[b'current'] = [node]
1080 hbisect.save_state(repo, state)
1080 hbisect.save_state(repo, state)
1081 status = ui.system(
1081 status = ui.system(
1082 command,
1082 command,
1083 environ={b'HG_NODE': hex(node)},
1083 environ={b'HG_NODE': hex(node)},
1084 blockedtag=b'bisect_check',
1084 blockedtag=b'bisect_check',
1085 )
1085 )
1086 if status == 125:
1086 if status == 125:
1087 transition = b"skip"
1087 transition = b"skip"
1088 elif status == 0:
1088 elif status == 0:
1089 transition = b"good"
1089 transition = b"good"
1090 # status < 0 means process was killed
1090 # status < 0 means process was killed
1091 elif status == 127:
1091 elif status == 127:
1092 raise error.Abort(_(b"failed to execute %s") % command)
1092 raise error.Abort(_(b"failed to execute %s") % command)
1093 elif status < 0:
1093 elif status < 0:
1094 raise error.Abort(_(b"%s killed") % command)
1094 raise error.Abort(_(b"%s killed") % command)
1095 else:
1095 else:
1096 transition = b"bad"
1096 transition = b"bad"
1097 state[transition].append(node)
1097 state[transition].append(node)
1098 ctx = repo[node]
1098 ctx = repo[node]
1099 ui.status(
1099 ui.status(
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1101 )
1101 )
1102 hbisect.checkstate(state)
1102 hbisect.checkstate(state)
1103 # bisect
1103 # bisect
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1105 # update to next check
1105 # update to next check
1106 node = nodes[0]
1106 node = nodes[0]
1107 mayupdate(repo, node, show_stats=False)
1107 mayupdate(repo, node, show_stats=False)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1109 return
1109 return
1110
1110
1111 hbisect.checkstate(state)
1111 hbisect.checkstate(state)
1112
1112
1113 # actually bisect
1113 # actually bisect
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1115 if extend:
1115 if extend:
1116 if not changesets:
1116 if not changesets:
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1118 if extendnode is not None:
1118 if extendnode is not None:
1119 ui.write(
1119 ui.write(
1120 _(b"Extending search to changeset %d:%s\n")
1120 _(b"Extending search to changeset %d:%s\n")
1121 % (extendnode.rev(), extendnode)
1121 % (extendnode.rev(), extendnode)
1122 )
1122 )
1123 state[b'current'] = [extendnode.node()]
1123 state[b'current'] = [extendnode.node()]
1124 hbisect.save_state(repo, state)
1124 hbisect.save_state(repo, state)
1125 return mayupdate(repo, extendnode.node())
1125 return mayupdate(repo, extendnode.node())
1126 raise error.Abort(_(b"nothing to extend"))
1126 raise error.Abort(_(b"nothing to extend"))
1127
1127
1128 if changesets == 0:
1128 if changesets == 0:
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1130 else:
1130 else:
1131 assert len(nodes) == 1 # only a single node can be tested next
1131 assert len(nodes) == 1 # only a single node can be tested next
1132 node = nodes[0]
1132 node = nodes[0]
1133 # compute the approximate number of remaining tests
1133 # compute the approximate number of remaining tests
1134 tests, size = 0, 2
1134 tests, size = 0, 2
1135 while size <= changesets:
1135 while size <= changesets:
1136 tests, size = tests + 1, size * 2
1136 tests, size = tests + 1, size * 2
1137 rev = repo.changelog.rev(node)
1137 rev = repo.changelog.rev(node)
1138 ui.write(
1138 ui.write(
1139 _(
1139 _(
1140 b"Testing changeset %d:%s "
1140 b"Testing changeset %d:%s "
1141 b"(%d changesets remaining, ~%d tests)\n"
1141 b"(%d changesets remaining, ~%d tests)\n"
1142 )
1142 )
1143 % (rev, short(node), changesets, tests)
1143 % (rev, short(node), changesets, tests)
1144 )
1144 )
1145 state[b'current'] = [node]
1145 state[b'current'] = [node]
1146 hbisect.save_state(repo, state)
1146 hbisect.save_state(repo, state)
1147 return mayupdate(repo, node)
1147 return mayupdate(repo, node)
1148
1148
1149
1149
1150 @command(
1150 @command(
1151 b'bookmarks|bookmark',
1151 b'bookmarks|bookmark',
1152 [
1152 [
1153 (b'f', b'force', False, _(b'force')),
1153 (b'f', b'force', False, _(b'force')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1159 ]
1159 ]
1160 + formatteropts,
1160 + formatteropts,
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1163 )
1163 )
1164 def bookmark(ui, repo, *names, **opts):
1164 def bookmark(ui, repo, *names, **opts):
1165 '''create a new bookmark or list existing bookmarks
1165 '''create a new bookmark or list existing bookmarks
1166
1166
1167 Bookmarks are labels on changesets to help track lines of development.
1167 Bookmarks are labels on changesets to help track lines of development.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1170
1170
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1172 The active bookmark is indicated with a '*'.
1172 The active bookmark is indicated with a '*'.
1173 When a commit is made, the active bookmark will advance to the new commit.
1173 When a commit is made, the active bookmark will advance to the new commit.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1175 Updating away from a bookmark will cause it to be deactivated.
1175 Updating away from a bookmark will cause it to be deactivated.
1176
1176
1177 Bookmarks can be pushed and pulled between repositories (see
1177 Bookmarks can be pushed and pulled between repositories (see
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1180 be created. Using :hg:`merge` will resolve the divergence.
1180 be created. Using :hg:`merge` will resolve the divergence.
1181
1181
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1183 the active bookmark's name.
1183 the active bookmark's name.
1184
1184
1185 A bookmark named '@' has the special property that :hg:`clone` will
1185 A bookmark named '@' has the special property that :hg:`clone` will
1186 check it out by default if it exists.
1186 check it out by default if it exists.
1187
1187
1188 .. container:: verbose
1188 .. container:: verbose
1189
1189
1190 Template:
1190 Template:
1191
1191
1192 The following keywords are supported in addition to the common template
1192 The following keywords are supported in addition to the common template
1193 keywords and functions such as ``{bookmark}``. See also
1193 keywords and functions such as ``{bookmark}``. See also
1194 :hg:`help templates`.
1194 :hg:`help templates`.
1195
1195
1196 :active: Boolean. True if the bookmark is active.
1196 :active: Boolean. True if the bookmark is active.
1197
1197
1198 Examples:
1198 Examples:
1199
1199
1200 - create an active bookmark for a new line of development::
1200 - create an active bookmark for a new line of development::
1201
1201
1202 hg book new-feature
1202 hg book new-feature
1203
1203
1204 - create an inactive bookmark as a place marker::
1204 - create an inactive bookmark as a place marker::
1205
1205
1206 hg book -i reviewed
1206 hg book -i reviewed
1207
1207
1208 - create an inactive bookmark on another changeset::
1208 - create an inactive bookmark on another changeset::
1209
1209
1210 hg book -r .^ tested
1210 hg book -r .^ tested
1211
1211
1212 - rename bookmark turkey to dinner::
1212 - rename bookmark turkey to dinner::
1213
1213
1214 hg book -m turkey dinner
1214 hg book -m turkey dinner
1215
1215
1216 - move the '@' bookmark from another branch::
1216 - move the '@' bookmark from another branch::
1217
1217
1218 hg book -f @
1218 hg book -f @
1219
1219
1220 - print only the active bookmark name::
1220 - print only the active bookmark name::
1221
1221
1222 hg book -ql .
1222 hg book -ql .
1223 '''
1223 '''
1224 opts = pycompat.byteskwargs(opts)
1224 opts = pycompat.byteskwargs(opts)
1225 force = opts.get(b'force')
1225 force = opts.get(b'force')
1226 rev = opts.get(b'rev')
1226 rev = opts.get(b'rev')
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1228
1228
1229 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1229 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1230 if action:
1230 if action:
1231 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1231 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1232 elif names or rev:
1232 elif names or rev:
1233 action = b'add'
1233 action = b'add'
1234 elif inactive:
1234 elif inactive:
1235 action = b'inactive' # meaning deactivate
1235 action = b'inactive' # meaning deactivate
1236 else:
1236 else:
1237 action = b'list'
1237 action = b'list'
1238
1238
1239 cmdutil.check_incompatible_arguments(
1239 cmdutil.check_incompatible_arguments(
1240 opts, b'inactive', [b'delete', b'list']
1240 opts, b'inactive', [b'delete', b'list']
1241 )
1241 )
1242 if not names and action in {b'add', b'delete'}:
1242 if not names and action in {b'add', b'delete'}:
1243 raise error.Abort(_(b"bookmark name required"))
1243 raise error.Abort(_(b"bookmark name required"))
1244
1244
1245 if action in {b'add', b'delete', b'rename', b'inactive'}:
1245 if action in {b'add', b'delete', b'rename', b'inactive'}:
1246 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1246 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1247 if action == b'delete':
1247 if action == b'delete':
1248 names = pycompat.maplist(repo._bookmarks.expandname, names)
1248 names = pycompat.maplist(repo._bookmarks.expandname, names)
1249 bookmarks.delete(repo, tr, names)
1249 bookmarks.delete(repo, tr, names)
1250 elif action == b'rename':
1250 elif action == b'rename':
1251 if not names:
1251 if not names:
1252 raise error.Abort(_(b"new bookmark name required"))
1252 raise error.Abort(_(b"new bookmark name required"))
1253 elif len(names) > 1:
1253 elif len(names) > 1:
1254 raise error.Abort(_(b"only one new bookmark name allowed"))
1254 raise error.Abort(_(b"only one new bookmark name allowed"))
1255 oldname = repo._bookmarks.expandname(opts[b'rename'])
1255 oldname = repo._bookmarks.expandname(opts[b'rename'])
1256 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1256 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1257 elif action == b'add':
1257 elif action == b'add':
1258 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1258 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1259 elif action == b'inactive':
1259 elif action == b'inactive':
1260 if len(repo._bookmarks) == 0:
1260 if len(repo._bookmarks) == 0:
1261 ui.status(_(b"no bookmarks set\n"))
1261 ui.status(_(b"no bookmarks set\n"))
1262 elif not repo._activebookmark:
1262 elif not repo._activebookmark:
1263 ui.status(_(b"no active bookmark\n"))
1263 ui.status(_(b"no active bookmark\n"))
1264 else:
1264 else:
1265 bookmarks.deactivate(repo)
1265 bookmarks.deactivate(repo)
1266 elif action == b'list':
1266 elif action == b'list':
1267 names = pycompat.maplist(repo._bookmarks.expandname, names)
1267 names = pycompat.maplist(repo._bookmarks.expandname, names)
1268 with ui.formatter(b'bookmarks', opts) as fm:
1268 with ui.formatter(b'bookmarks', opts) as fm:
1269 bookmarks.printbookmarks(ui, repo, fm, names)
1269 bookmarks.printbookmarks(ui, repo, fm, names)
1270 else:
1270 else:
1271 raise error.ProgrammingError(b'invalid action: %s' % action)
1271 raise error.ProgrammingError(b'invalid action: %s' % action)
1272
1272
1273
1273
1274 @command(
1274 @command(
1275 b'branch',
1275 b'branch',
1276 [
1276 [
1277 (
1277 (
1278 b'f',
1278 b'f',
1279 b'force',
1279 b'force',
1280 None,
1280 None,
1281 _(b'set branch name even if it shadows an existing branch'),
1281 _(b'set branch name even if it shadows an existing branch'),
1282 ),
1282 ),
1283 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1283 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1284 (
1284 (
1285 b'r',
1285 b'r',
1286 b'rev',
1286 b'rev',
1287 [],
1287 [],
1288 _(b'change branches of the given revs (EXPERIMENTAL)'),
1288 _(b'change branches of the given revs (EXPERIMENTAL)'),
1289 ),
1289 ),
1290 ],
1290 ],
1291 _(b'[-fC] [NAME]'),
1291 _(b'[-fC] [NAME]'),
1292 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1292 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1293 )
1293 )
1294 def branch(ui, repo, label=None, **opts):
1294 def branch(ui, repo, label=None, **opts):
1295 """set or show the current branch name
1295 """set or show the current branch name
1296
1296
1297 .. note::
1297 .. note::
1298
1298
1299 Branch names are permanent and global. Use :hg:`bookmark` to create a
1299 Branch names are permanent and global. Use :hg:`bookmark` to create a
1300 light-weight bookmark instead. See :hg:`help glossary` for more
1300 light-weight bookmark instead. See :hg:`help glossary` for more
1301 information about named branches and bookmarks.
1301 information about named branches and bookmarks.
1302
1302
1303 With no argument, show the current branch name. With one argument,
1303 With no argument, show the current branch name. With one argument,
1304 set the working directory branch name (the branch will not exist
1304 set the working directory branch name (the branch will not exist
1305 in the repository until the next commit). Standard practice
1305 in the repository until the next commit). Standard practice
1306 recommends that primary development take place on the 'default'
1306 recommends that primary development take place on the 'default'
1307 branch.
1307 branch.
1308
1308
1309 Unless -f/--force is specified, branch will not let you set a
1309 Unless -f/--force is specified, branch will not let you set a
1310 branch name that already exists.
1310 branch name that already exists.
1311
1311
1312 Use -C/--clean to reset the working directory branch to that of
1312 Use -C/--clean to reset the working directory branch to that of
1313 the parent of the working directory, negating a previous branch
1313 the parent of the working directory, negating a previous branch
1314 change.
1314 change.
1315
1315
1316 Use the command :hg:`update` to switch to an existing branch. Use
1316 Use the command :hg:`update` to switch to an existing branch. Use
1317 :hg:`commit --close-branch` to mark this branch head as closed.
1317 :hg:`commit --close-branch` to mark this branch head as closed.
1318 When all heads of a branch are closed, the branch will be
1318 When all heads of a branch are closed, the branch will be
1319 considered closed.
1319 considered closed.
1320
1320
1321 Returns 0 on success.
1321 Returns 0 on success.
1322 """
1322 """
1323 opts = pycompat.byteskwargs(opts)
1323 opts = pycompat.byteskwargs(opts)
1324 revs = opts.get(b'rev')
1324 revs = opts.get(b'rev')
1325 if label:
1325 if label:
1326 label = label.strip()
1326 label = label.strip()
1327
1327
1328 if not opts.get(b'clean') and not label:
1328 if not opts.get(b'clean') and not label:
1329 if revs:
1329 if revs:
1330 raise error.Abort(_(b"no branch name specified for the revisions"))
1330 raise error.Abort(_(b"no branch name specified for the revisions"))
1331 ui.write(b"%s\n" % repo.dirstate.branch())
1331 ui.write(b"%s\n" % repo.dirstate.branch())
1332 return
1332 return
1333
1333
1334 with repo.wlock():
1334 with repo.wlock():
1335 if opts.get(b'clean'):
1335 if opts.get(b'clean'):
1336 label = repo[b'.'].branch()
1336 label = repo[b'.'].branch()
1337 repo.dirstate.setbranch(label)
1337 repo.dirstate.setbranch(label)
1338 ui.status(_(b'reset working directory to branch %s\n') % label)
1338 ui.status(_(b'reset working directory to branch %s\n') % label)
1339 elif label:
1339 elif label:
1340
1340
1341 scmutil.checknewlabel(repo, label, b'branch')
1341 scmutil.checknewlabel(repo, label, b'branch')
1342 if revs:
1342 if revs:
1343 return cmdutil.changebranch(ui, repo, revs, label, opts)
1343 return cmdutil.changebranch(ui, repo, revs, label, opts)
1344
1344
1345 if not opts.get(b'force') and label in repo.branchmap():
1345 if not opts.get(b'force') and label in repo.branchmap():
1346 if label not in [p.branch() for p in repo[None].parents()]:
1346 if label not in [p.branch() for p in repo[None].parents()]:
1347 raise error.Abort(
1347 raise error.Abort(
1348 _(b'a branch of the same name already exists'),
1348 _(b'a branch of the same name already exists'),
1349 # i18n: "it" refers to an existing branch
1349 # i18n: "it" refers to an existing branch
1350 hint=_(b"use 'hg update' to switch to it"),
1350 hint=_(b"use 'hg update' to switch to it"),
1351 )
1351 )
1352
1352
1353 repo.dirstate.setbranch(label)
1353 repo.dirstate.setbranch(label)
1354 ui.status(_(b'marked working directory as branch %s\n') % label)
1354 ui.status(_(b'marked working directory as branch %s\n') % label)
1355
1355
1356 # find any open named branches aside from default
1356 # find any open named branches aside from default
1357 for n, h, t, c in repo.branchmap().iterbranches():
1357 for n, h, t, c in repo.branchmap().iterbranches():
1358 if n != b"default" and not c:
1358 if n != b"default" and not c:
1359 return 0
1359 return 0
1360 ui.status(
1360 ui.status(
1361 _(
1361 _(
1362 b'(branches are permanent and global, '
1362 b'(branches are permanent and global, '
1363 b'did you want a bookmark?)\n'
1363 b'did you want a bookmark?)\n'
1364 )
1364 )
1365 )
1365 )
1366
1366
1367
1367
1368 @command(
1368 @command(
1369 b'branches',
1369 b'branches',
1370 [
1370 [
1371 (
1371 (
1372 b'a',
1372 b'a',
1373 b'active',
1373 b'active',
1374 False,
1374 False,
1375 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1375 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1376 ),
1376 ),
1377 (b'c', b'closed', False, _(b'show normal and closed branches')),
1377 (b'c', b'closed', False, _(b'show normal and closed branches')),
1378 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1378 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1379 ]
1379 ]
1380 + formatteropts,
1380 + formatteropts,
1381 _(b'[-c]'),
1381 _(b'[-c]'),
1382 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1382 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1383 intents={INTENT_READONLY},
1383 intents={INTENT_READONLY},
1384 )
1384 )
1385 def branches(ui, repo, active=False, closed=False, **opts):
1385 def branches(ui, repo, active=False, closed=False, **opts):
1386 """list repository named branches
1386 """list repository named branches
1387
1387
1388 List the repository's named branches, indicating which ones are
1388 List the repository's named branches, indicating which ones are
1389 inactive. If -c/--closed is specified, also list branches which have
1389 inactive. If -c/--closed is specified, also list branches which have
1390 been marked closed (see :hg:`commit --close-branch`).
1390 been marked closed (see :hg:`commit --close-branch`).
1391
1391
1392 Use the command :hg:`update` to switch to an existing branch.
1392 Use the command :hg:`update` to switch to an existing branch.
1393
1393
1394 .. container:: verbose
1394 .. container:: verbose
1395
1395
1396 Template:
1396 Template:
1397
1397
1398 The following keywords are supported in addition to the common template
1398 The following keywords are supported in addition to the common template
1399 keywords and functions such as ``{branch}``. See also
1399 keywords and functions such as ``{branch}``. See also
1400 :hg:`help templates`.
1400 :hg:`help templates`.
1401
1401
1402 :active: Boolean. True if the branch is active.
1402 :active: Boolean. True if the branch is active.
1403 :closed: Boolean. True if the branch is closed.
1403 :closed: Boolean. True if the branch is closed.
1404 :current: Boolean. True if it is the current branch.
1404 :current: Boolean. True if it is the current branch.
1405
1405
1406 Returns 0.
1406 Returns 0.
1407 """
1407 """
1408
1408
1409 opts = pycompat.byteskwargs(opts)
1409 opts = pycompat.byteskwargs(opts)
1410 revs = opts.get(b'rev')
1410 revs = opts.get(b'rev')
1411 selectedbranches = None
1411 selectedbranches = None
1412 if revs:
1412 if revs:
1413 revs = scmutil.revrange(repo, revs)
1413 revs = scmutil.revrange(repo, revs)
1414 getbi = repo.revbranchcache().branchinfo
1414 getbi = repo.revbranchcache().branchinfo
1415 selectedbranches = {getbi(r)[0] for r in revs}
1415 selectedbranches = {getbi(r)[0] for r in revs}
1416
1416
1417 ui.pager(b'branches')
1417 ui.pager(b'branches')
1418 fm = ui.formatter(b'branches', opts)
1418 fm = ui.formatter(b'branches', opts)
1419 hexfunc = fm.hexfunc
1419 hexfunc = fm.hexfunc
1420
1420
1421 allheads = set(repo.heads())
1421 allheads = set(repo.heads())
1422 branches = []
1422 branches = []
1423 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1423 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1424 if selectedbranches is not None and tag not in selectedbranches:
1424 if selectedbranches is not None and tag not in selectedbranches:
1425 continue
1425 continue
1426 isactive = False
1426 isactive = False
1427 if not isclosed:
1427 if not isclosed:
1428 openheads = set(repo.branchmap().iteropen(heads))
1428 openheads = set(repo.branchmap().iteropen(heads))
1429 isactive = bool(openheads & allheads)
1429 isactive = bool(openheads & allheads)
1430 branches.append((tag, repo[tip], isactive, not isclosed))
1430 branches.append((tag, repo[tip], isactive, not isclosed))
1431 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1431 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1432
1432
1433 for tag, ctx, isactive, isopen in branches:
1433 for tag, ctx, isactive, isopen in branches:
1434 if active and not isactive:
1434 if active and not isactive:
1435 continue
1435 continue
1436 if isactive:
1436 if isactive:
1437 label = b'branches.active'
1437 label = b'branches.active'
1438 notice = b''
1438 notice = b''
1439 elif not isopen:
1439 elif not isopen:
1440 if not closed:
1440 if not closed:
1441 continue
1441 continue
1442 label = b'branches.closed'
1442 label = b'branches.closed'
1443 notice = _(b' (closed)')
1443 notice = _(b' (closed)')
1444 else:
1444 else:
1445 label = b'branches.inactive'
1445 label = b'branches.inactive'
1446 notice = _(b' (inactive)')
1446 notice = _(b' (inactive)')
1447 current = tag == repo.dirstate.branch()
1447 current = tag == repo.dirstate.branch()
1448 if current:
1448 if current:
1449 label = b'branches.current'
1449 label = b'branches.current'
1450
1450
1451 fm.startitem()
1451 fm.startitem()
1452 fm.write(b'branch', b'%s', tag, label=label)
1452 fm.write(b'branch', b'%s', tag, label=label)
1453 rev = ctx.rev()
1453 rev = ctx.rev()
1454 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1454 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1455 fmt = b' ' * padsize + b' %d:%s'
1455 fmt = b' ' * padsize + b' %d:%s'
1456 fm.condwrite(
1456 fm.condwrite(
1457 not ui.quiet,
1457 not ui.quiet,
1458 b'rev node',
1458 b'rev node',
1459 fmt,
1459 fmt,
1460 rev,
1460 rev,
1461 hexfunc(ctx.node()),
1461 hexfunc(ctx.node()),
1462 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1462 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1463 )
1463 )
1464 fm.context(ctx=ctx)
1464 fm.context(ctx=ctx)
1465 fm.data(active=isactive, closed=not isopen, current=current)
1465 fm.data(active=isactive, closed=not isopen, current=current)
1466 if not ui.quiet:
1466 if not ui.quiet:
1467 fm.plain(notice)
1467 fm.plain(notice)
1468 fm.plain(b'\n')
1468 fm.plain(b'\n')
1469 fm.end()
1469 fm.end()
1470
1470
1471
1471
1472 @command(
1472 @command(
1473 b'bundle',
1473 b'bundle',
1474 [
1474 [
1475 (
1475 (
1476 b'f',
1476 b'f',
1477 b'force',
1477 b'force',
1478 None,
1478 None,
1479 _(b'run even when the destination is unrelated'),
1479 _(b'run even when the destination is unrelated'),
1480 ),
1480 ),
1481 (
1481 (
1482 b'r',
1482 b'r',
1483 b'rev',
1483 b'rev',
1484 [],
1484 [],
1485 _(b'a changeset intended to be added to the destination'),
1485 _(b'a changeset intended to be added to the destination'),
1486 _(b'REV'),
1486 _(b'REV'),
1487 ),
1487 ),
1488 (
1488 (
1489 b'b',
1489 b'b',
1490 b'branch',
1490 b'branch',
1491 [],
1491 [],
1492 _(b'a specific branch you would like to bundle'),
1492 _(b'a specific branch you would like to bundle'),
1493 _(b'BRANCH'),
1493 _(b'BRANCH'),
1494 ),
1494 ),
1495 (
1495 (
1496 b'',
1496 b'',
1497 b'base',
1497 b'base',
1498 [],
1498 [],
1499 _(b'a base changeset assumed to be available at the destination'),
1499 _(b'a base changeset assumed to be available at the destination'),
1500 _(b'REV'),
1500 _(b'REV'),
1501 ),
1501 ),
1502 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1502 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1503 (
1503 (
1504 b't',
1504 b't',
1505 b'type',
1505 b'type',
1506 b'bzip2',
1506 b'bzip2',
1507 _(b'bundle compression type to use'),
1507 _(b'bundle compression type to use'),
1508 _(b'TYPE'),
1508 _(b'TYPE'),
1509 ),
1509 ),
1510 ]
1510 ]
1511 + remoteopts,
1511 + remoteopts,
1512 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1512 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1513 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1513 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1514 )
1514 )
1515 def bundle(ui, repo, fname, dest=None, **opts):
1515 def bundle(ui, repo, fname, dest=None, **opts):
1516 """create a bundle file
1516 """create a bundle file
1517
1517
1518 Generate a bundle file containing data to be transferred to another
1518 Generate a bundle file containing data to be transferred to another
1519 repository.
1519 repository.
1520
1520
1521 To create a bundle containing all changesets, use -a/--all
1521 To create a bundle containing all changesets, use -a/--all
1522 (or --base null). Otherwise, hg assumes the destination will have
1522 (or --base null). Otherwise, hg assumes the destination will have
1523 all the nodes you specify with --base parameters. Otherwise, hg
1523 all the nodes you specify with --base parameters. Otherwise, hg
1524 will assume the repository has all the nodes in destination, or
1524 will assume the repository has all the nodes in destination, or
1525 default-push/default if no destination is specified, where destination
1525 default-push/default if no destination is specified, where destination
1526 is the repository you provide through DEST option.
1526 is the repository you provide through DEST option.
1527
1527
1528 You can change bundle format with the -t/--type option. See
1528 You can change bundle format with the -t/--type option. See
1529 :hg:`help bundlespec` for documentation on this format. By default,
1529 :hg:`help bundlespec` for documentation on this format. By default,
1530 the most appropriate format is used and compression defaults to
1530 the most appropriate format is used and compression defaults to
1531 bzip2.
1531 bzip2.
1532
1532
1533 The bundle file can then be transferred using conventional means
1533 The bundle file can then be transferred using conventional means
1534 and applied to another repository with the unbundle or pull
1534 and applied to another repository with the unbundle or pull
1535 command. This is useful when direct push and pull are not
1535 command. This is useful when direct push and pull are not
1536 available or when exporting an entire repository is undesirable.
1536 available or when exporting an entire repository is undesirable.
1537
1537
1538 Applying bundles preserves all changeset contents including
1538 Applying bundles preserves all changeset contents including
1539 permissions, copy/rename information, and revision history.
1539 permissions, copy/rename information, and revision history.
1540
1540
1541 Returns 0 on success, 1 if no changes found.
1541 Returns 0 on success, 1 if no changes found.
1542 """
1542 """
1543 opts = pycompat.byteskwargs(opts)
1543 opts = pycompat.byteskwargs(opts)
1544 revs = None
1544 revs = None
1545 if b'rev' in opts:
1545 if b'rev' in opts:
1546 revstrings = opts[b'rev']
1546 revstrings = opts[b'rev']
1547 revs = scmutil.revrange(repo, revstrings)
1547 revs = scmutil.revrange(repo, revstrings)
1548 if revstrings and not revs:
1548 if revstrings and not revs:
1549 raise error.Abort(_(b'no commits to bundle'))
1549 raise error.Abort(_(b'no commits to bundle'))
1550
1550
1551 bundletype = opts.get(b'type', b'bzip2').lower()
1551 bundletype = opts.get(b'type', b'bzip2').lower()
1552 try:
1552 try:
1553 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1553 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1554 except error.UnsupportedBundleSpecification as e:
1554 except error.UnsupportedBundleSpecification as e:
1555 raise error.Abort(
1555 raise error.Abort(
1556 pycompat.bytestr(e),
1556 pycompat.bytestr(e),
1557 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1557 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1558 )
1558 )
1559 cgversion = bundlespec.contentopts[b"cg.version"]
1559 cgversion = bundlespec.contentopts[b"cg.version"]
1560
1560
1561 # Packed bundles are a pseudo bundle format for now.
1561 # Packed bundles are a pseudo bundle format for now.
1562 if cgversion == b's1':
1562 if cgversion == b's1':
1563 raise error.Abort(
1563 raise error.Abort(
1564 _(b'packed bundles cannot be produced by "hg bundle"'),
1564 _(b'packed bundles cannot be produced by "hg bundle"'),
1565 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1565 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1566 )
1566 )
1567
1567
1568 if opts.get(b'all'):
1568 if opts.get(b'all'):
1569 if dest:
1569 if dest:
1570 raise error.Abort(
1570 raise error.Abort(
1571 _(b"--all is incompatible with specifying a destination")
1571 _(b"--all is incompatible with specifying a destination")
1572 )
1572 )
1573 if opts.get(b'base'):
1573 if opts.get(b'base'):
1574 ui.warn(_(b"ignoring --base because --all was specified\n"))
1574 ui.warn(_(b"ignoring --base because --all was specified\n"))
1575 base = [nullrev]
1575 base = [nullrev]
1576 else:
1576 else:
1577 base = scmutil.revrange(repo, opts.get(b'base'))
1577 base = scmutil.revrange(repo, opts.get(b'base'))
1578 if cgversion not in changegroup.supportedoutgoingversions(repo):
1578 if cgversion not in changegroup.supportedoutgoingversions(repo):
1579 raise error.Abort(
1579 raise error.Abort(
1580 _(b"repository does not support bundle version %s") % cgversion
1580 _(b"repository does not support bundle version %s") % cgversion
1581 )
1581 )
1582
1582
1583 if base:
1583 if base:
1584 if dest:
1584 if dest:
1585 raise error.Abort(
1585 raise error.Abort(
1586 _(b"--base is incompatible with specifying a destination")
1586 _(b"--base is incompatible with specifying a destination")
1587 )
1587 )
1588 common = [repo[rev].node() for rev in base]
1588 common = [repo[rev].node() for rev in base]
1589 heads = [repo[r].node() for r in revs] if revs else None
1589 heads = [repo[r].node() for r in revs] if revs else None
1590 outgoing = discovery.outgoing(repo, common, heads)
1590 outgoing = discovery.outgoing(repo, common, heads)
1591 else:
1591 else:
1592 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1592 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1593 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1593 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1594 other = hg.peer(repo, opts, dest)
1594 other = hg.peer(repo, opts, dest)
1595 revs = [repo[r].hex() for r in revs]
1595 revs = [repo[r].hex() for r in revs]
1596 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1596 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1597 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1597 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1598 outgoing = discovery.findcommonoutgoing(
1598 outgoing = discovery.findcommonoutgoing(
1599 repo,
1599 repo,
1600 other,
1600 other,
1601 onlyheads=heads,
1601 onlyheads=heads,
1602 force=opts.get(b'force'),
1602 force=opts.get(b'force'),
1603 portable=True,
1603 portable=True,
1604 )
1604 )
1605
1605
1606 if not outgoing.missing:
1606 if not outgoing.missing:
1607 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1607 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1608 return 1
1608 return 1
1609
1609
1610 if cgversion == b'01': # bundle1
1610 if cgversion == b'01': # bundle1
1611 bversion = b'HG10' + bundlespec.wirecompression
1611 bversion = b'HG10' + bundlespec.wirecompression
1612 bcompression = None
1612 bcompression = None
1613 elif cgversion in (b'02', b'03'):
1613 elif cgversion in (b'02', b'03'):
1614 bversion = b'HG20'
1614 bversion = b'HG20'
1615 bcompression = bundlespec.wirecompression
1615 bcompression = bundlespec.wirecompression
1616 else:
1616 else:
1617 raise error.ProgrammingError(
1617 raise error.ProgrammingError(
1618 b'bundle: unexpected changegroup version %s' % cgversion
1618 b'bundle: unexpected changegroup version %s' % cgversion
1619 )
1619 )
1620
1620
1621 # TODO compression options should be derived from bundlespec parsing.
1621 # TODO compression options should be derived from bundlespec parsing.
1622 # This is a temporary hack to allow adjusting bundle compression
1622 # This is a temporary hack to allow adjusting bundle compression
1623 # level without a) formalizing the bundlespec changes to declare it
1623 # level without a) formalizing the bundlespec changes to declare it
1624 # b) introducing a command flag.
1624 # b) introducing a command flag.
1625 compopts = {}
1625 compopts = {}
1626 complevel = ui.configint(
1626 complevel = ui.configint(
1627 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1627 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1628 )
1628 )
1629 if complevel is None:
1629 if complevel is None:
1630 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1630 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1631 if complevel is not None:
1631 if complevel is not None:
1632 compopts[b'level'] = complevel
1632 compopts[b'level'] = complevel
1633
1633
1634 # Allow overriding the bundling of obsmarker in phases through
1634 # Allow overriding the bundling of obsmarker in phases through
1635 # configuration while we don't have a bundle version that include them
1635 # configuration while we don't have a bundle version that include them
1636 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1636 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1637 bundlespec.contentopts[b'obsolescence'] = True
1637 bundlespec.contentopts[b'obsolescence'] = True
1638 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1638 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1639 bundlespec.contentopts[b'phases'] = True
1639 bundlespec.contentopts[b'phases'] = True
1640
1640
1641 bundle2.writenewbundle(
1641 bundle2.writenewbundle(
1642 ui,
1642 ui,
1643 repo,
1643 repo,
1644 b'bundle',
1644 b'bundle',
1645 fname,
1645 fname,
1646 bversion,
1646 bversion,
1647 outgoing,
1647 outgoing,
1648 bundlespec.contentopts,
1648 bundlespec.contentopts,
1649 compression=bcompression,
1649 compression=bcompression,
1650 compopts=compopts,
1650 compopts=compopts,
1651 )
1651 )
1652
1652
1653
1653
1654 @command(
1654 @command(
1655 b'cat',
1655 b'cat',
1656 [
1656 [
1657 (
1657 (
1658 b'o',
1658 b'o',
1659 b'output',
1659 b'output',
1660 b'',
1660 b'',
1661 _(b'print output to file with formatted name'),
1661 _(b'print output to file with formatted name'),
1662 _(b'FORMAT'),
1662 _(b'FORMAT'),
1663 ),
1663 ),
1664 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1664 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1665 (b'', b'decode', None, _(b'apply any matching decode filter')),
1665 (b'', b'decode', None, _(b'apply any matching decode filter')),
1666 ]
1666 ]
1667 + walkopts
1667 + walkopts
1668 + formatteropts,
1668 + formatteropts,
1669 _(b'[OPTION]... FILE...'),
1669 _(b'[OPTION]... FILE...'),
1670 helpcategory=command.CATEGORY_FILE_CONTENTS,
1670 helpcategory=command.CATEGORY_FILE_CONTENTS,
1671 inferrepo=True,
1671 inferrepo=True,
1672 intents={INTENT_READONLY},
1672 intents={INTENT_READONLY},
1673 )
1673 )
1674 def cat(ui, repo, file1, *pats, **opts):
1674 def cat(ui, repo, file1, *pats, **opts):
1675 """output the current or given revision of files
1675 """output the current or given revision of files
1676
1676
1677 Print the specified files as they were at the given revision. If
1677 Print the specified files as they were at the given revision. If
1678 no revision is given, the parent of the working directory is used.
1678 no revision is given, the parent of the working directory is used.
1679
1679
1680 Output may be to a file, in which case the name of the file is
1680 Output may be to a file, in which case the name of the file is
1681 given using a template string. See :hg:`help templates`. In addition
1681 given using a template string. See :hg:`help templates`. In addition
1682 to the common template keywords, the following formatting rules are
1682 to the common template keywords, the following formatting rules are
1683 supported:
1683 supported:
1684
1684
1685 :``%%``: literal "%" character
1685 :``%%``: literal "%" character
1686 :``%s``: basename of file being printed
1686 :``%s``: basename of file being printed
1687 :``%d``: dirname of file being printed, or '.' if in repository root
1687 :``%d``: dirname of file being printed, or '.' if in repository root
1688 :``%p``: root-relative path name of file being printed
1688 :``%p``: root-relative path name of file being printed
1689 :``%H``: changeset hash (40 hexadecimal digits)
1689 :``%H``: changeset hash (40 hexadecimal digits)
1690 :``%R``: changeset revision number
1690 :``%R``: changeset revision number
1691 :``%h``: short-form changeset hash (12 hexadecimal digits)
1691 :``%h``: short-form changeset hash (12 hexadecimal digits)
1692 :``%r``: zero-padded changeset revision number
1692 :``%r``: zero-padded changeset revision number
1693 :``%b``: basename of the exporting repository
1693 :``%b``: basename of the exporting repository
1694 :``\\``: literal "\\" character
1694 :``\\``: literal "\\" character
1695
1695
1696 .. container:: verbose
1696 .. container:: verbose
1697
1697
1698 Template:
1698 Template:
1699
1699
1700 The following keywords are supported in addition to the common template
1700 The following keywords are supported in addition to the common template
1701 keywords and functions. See also :hg:`help templates`.
1701 keywords and functions. See also :hg:`help templates`.
1702
1702
1703 :data: String. File content.
1703 :data: String. File content.
1704 :path: String. Repository-absolute path of the file.
1704 :path: String. Repository-absolute path of the file.
1705
1705
1706 Returns 0 on success.
1706 Returns 0 on success.
1707 """
1707 """
1708 opts = pycompat.byteskwargs(opts)
1708 opts = pycompat.byteskwargs(opts)
1709 rev = opts.get(b'rev')
1709 rev = opts.get(b'rev')
1710 if rev:
1710 if rev:
1711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1712 ctx = scmutil.revsingle(repo, rev)
1712 ctx = scmutil.revsingle(repo, rev)
1713 m = scmutil.match(ctx, (file1,) + pats, opts)
1713 m = scmutil.match(ctx, (file1,) + pats, opts)
1714 fntemplate = opts.pop(b'output', b'')
1714 fntemplate = opts.pop(b'output', b'')
1715 if cmdutil.isstdiofilename(fntemplate):
1715 if cmdutil.isstdiofilename(fntemplate):
1716 fntemplate = b''
1716 fntemplate = b''
1717
1717
1718 if fntemplate:
1718 if fntemplate:
1719 fm = formatter.nullformatter(ui, b'cat', opts)
1719 fm = formatter.nullformatter(ui, b'cat', opts)
1720 else:
1720 else:
1721 ui.pager(b'cat')
1721 ui.pager(b'cat')
1722 fm = ui.formatter(b'cat', opts)
1722 fm = ui.formatter(b'cat', opts)
1723 with fm:
1723 with fm:
1724 return cmdutil.cat(
1724 return cmdutil.cat(
1725 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1725 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1726 )
1726 )
1727
1727
1728
1728
1729 @command(
1729 @command(
1730 b'clone',
1730 b'clone',
1731 [
1731 [
1732 (
1732 (
1733 b'U',
1733 b'U',
1734 b'noupdate',
1734 b'noupdate',
1735 None,
1735 None,
1736 _(
1736 _(
1737 b'the clone will include an empty working '
1737 b'the clone will include an empty working '
1738 b'directory (only a repository)'
1738 b'directory (only a repository)'
1739 ),
1739 ),
1740 ),
1740 ),
1741 (
1741 (
1742 b'u',
1742 b'u',
1743 b'updaterev',
1743 b'updaterev',
1744 b'',
1744 b'',
1745 _(b'revision, tag, or branch to check out'),
1745 _(b'revision, tag, or branch to check out'),
1746 _(b'REV'),
1746 _(b'REV'),
1747 ),
1747 ),
1748 (
1748 (
1749 b'r',
1749 b'r',
1750 b'rev',
1750 b'rev',
1751 [],
1751 [],
1752 _(
1752 _(
1753 b'do not clone everything, but include this changeset'
1753 b'do not clone everything, but include this changeset'
1754 b' and its ancestors'
1754 b' and its ancestors'
1755 ),
1755 ),
1756 _(b'REV'),
1756 _(b'REV'),
1757 ),
1757 ),
1758 (
1758 (
1759 b'b',
1759 b'b',
1760 b'branch',
1760 b'branch',
1761 [],
1761 [],
1762 _(
1762 _(
1763 b'do not clone everything, but include this branch\'s'
1763 b'do not clone everything, but include this branch\'s'
1764 b' changesets and their ancestors'
1764 b' changesets and their ancestors'
1765 ),
1765 ),
1766 _(b'BRANCH'),
1766 _(b'BRANCH'),
1767 ),
1767 ),
1768 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1768 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1769 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1769 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1770 (b'', b'stream', None, _(b'clone with minimal data processing')),
1770 (b'', b'stream', None, _(b'clone with minimal data processing')),
1771 ]
1771 ]
1772 + remoteopts,
1772 + remoteopts,
1773 _(b'[OPTION]... SOURCE [DEST]'),
1773 _(b'[OPTION]... SOURCE [DEST]'),
1774 helpcategory=command.CATEGORY_REPO_CREATION,
1774 helpcategory=command.CATEGORY_REPO_CREATION,
1775 helpbasic=True,
1775 helpbasic=True,
1776 norepo=True,
1776 norepo=True,
1777 )
1777 )
1778 def clone(ui, source, dest=None, **opts):
1778 def clone(ui, source, dest=None, **opts):
1779 """make a copy of an existing repository
1779 """make a copy of an existing repository
1780
1780
1781 Create a copy of an existing repository in a new directory.
1781 Create a copy of an existing repository in a new directory.
1782
1782
1783 If no destination directory name is specified, it defaults to the
1783 If no destination directory name is specified, it defaults to the
1784 basename of the source.
1784 basename of the source.
1785
1785
1786 The location of the source is added to the new repository's
1786 The location of the source is added to the new repository's
1787 ``.hg/hgrc`` file, as the default to be used for future pulls.
1787 ``.hg/hgrc`` file, as the default to be used for future pulls.
1788
1788
1789 Only local paths and ``ssh://`` URLs are supported as
1789 Only local paths and ``ssh://`` URLs are supported as
1790 destinations. For ``ssh://`` destinations, no working directory or
1790 destinations. For ``ssh://`` destinations, no working directory or
1791 ``.hg/hgrc`` will be created on the remote side.
1791 ``.hg/hgrc`` will be created on the remote side.
1792
1792
1793 If the source repository has a bookmark called '@' set, that
1793 If the source repository has a bookmark called '@' set, that
1794 revision will be checked out in the new repository by default.
1794 revision will be checked out in the new repository by default.
1795
1795
1796 To check out a particular version, use -u/--update, or
1796 To check out a particular version, use -u/--update, or
1797 -U/--noupdate to create a clone with no working directory.
1797 -U/--noupdate to create a clone with no working directory.
1798
1798
1799 To pull only a subset of changesets, specify one or more revisions
1799 To pull only a subset of changesets, specify one or more revisions
1800 identifiers with -r/--rev or branches with -b/--branch. The
1800 identifiers with -r/--rev or branches with -b/--branch. The
1801 resulting clone will contain only the specified changesets and
1801 resulting clone will contain only the specified changesets and
1802 their ancestors. These options (or 'clone src#rev dest') imply
1802 their ancestors. These options (or 'clone src#rev dest') imply
1803 --pull, even for local source repositories.
1803 --pull, even for local source repositories.
1804
1804
1805 In normal clone mode, the remote normalizes repository data into a common
1805 In normal clone mode, the remote normalizes repository data into a common
1806 exchange format and the receiving end translates this data into its local
1806 exchange format and the receiving end translates this data into its local
1807 storage format. --stream activates a different clone mode that essentially
1807 storage format. --stream activates a different clone mode that essentially
1808 copies repository files from the remote with minimal data processing. This
1808 copies repository files from the remote with minimal data processing. This
1809 significantly reduces the CPU cost of a clone both remotely and locally.
1809 significantly reduces the CPU cost of a clone both remotely and locally.
1810 However, it often increases the transferred data size by 30-40%. This can
1810 However, it often increases the transferred data size by 30-40%. This can
1811 result in substantially faster clones where I/O throughput is plentiful,
1811 result in substantially faster clones where I/O throughput is plentiful,
1812 especially for larger repositories. A side-effect of --stream clones is
1812 especially for larger repositories. A side-effect of --stream clones is
1813 that storage settings and requirements on the remote are applied locally:
1813 that storage settings and requirements on the remote are applied locally:
1814 a modern client may inherit legacy or inefficient storage used by the
1814 a modern client may inherit legacy or inefficient storage used by the
1815 remote or a legacy Mercurial client may not be able to clone from a
1815 remote or a legacy Mercurial client may not be able to clone from a
1816 modern Mercurial remote.
1816 modern Mercurial remote.
1817
1817
1818 .. note::
1818 .. note::
1819
1819
1820 Specifying a tag will include the tagged changeset but not the
1820 Specifying a tag will include the tagged changeset but not the
1821 changeset containing the tag.
1821 changeset containing the tag.
1822
1822
1823 .. container:: verbose
1823 .. container:: verbose
1824
1824
1825 For efficiency, hardlinks are used for cloning whenever the
1825 For efficiency, hardlinks are used for cloning whenever the
1826 source and destination are on the same filesystem (note this
1826 source and destination are on the same filesystem (note this
1827 applies only to the repository data, not to the working
1827 applies only to the repository data, not to the working
1828 directory). Some filesystems, such as AFS, implement hardlinking
1828 directory). Some filesystems, such as AFS, implement hardlinking
1829 incorrectly, but do not report errors. In these cases, use the
1829 incorrectly, but do not report errors. In these cases, use the
1830 --pull option to avoid hardlinking.
1830 --pull option to avoid hardlinking.
1831
1831
1832 Mercurial will update the working directory to the first applicable
1832 Mercurial will update the working directory to the first applicable
1833 revision from this list:
1833 revision from this list:
1834
1834
1835 a) null if -U or the source repository has no changesets
1835 a) null if -U or the source repository has no changesets
1836 b) if -u . and the source repository is local, the first parent of
1836 b) if -u . and the source repository is local, the first parent of
1837 the source repository's working directory
1837 the source repository's working directory
1838 c) the changeset specified with -u (if a branch name, this means the
1838 c) the changeset specified with -u (if a branch name, this means the
1839 latest head of that branch)
1839 latest head of that branch)
1840 d) the changeset specified with -r
1840 d) the changeset specified with -r
1841 e) the tipmost head specified with -b
1841 e) the tipmost head specified with -b
1842 f) the tipmost head specified with the url#branch source syntax
1842 f) the tipmost head specified with the url#branch source syntax
1843 g) the revision marked with the '@' bookmark, if present
1843 g) the revision marked with the '@' bookmark, if present
1844 h) the tipmost head of the default branch
1844 h) the tipmost head of the default branch
1845 i) tip
1845 i) tip
1846
1846
1847 When cloning from servers that support it, Mercurial may fetch
1847 When cloning from servers that support it, Mercurial may fetch
1848 pre-generated data from a server-advertised URL or inline from the
1848 pre-generated data from a server-advertised URL or inline from the
1849 same stream. When this is done, hooks operating on incoming changesets
1849 same stream. When this is done, hooks operating on incoming changesets
1850 and changegroups may fire more than once, once for each pre-generated
1850 and changegroups may fire more than once, once for each pre-generated
1851 bundle and as well as for any additional remaining data. In addition,
1851 bundle and as well as for any additional remaining data. In addition,
1852 if an error occurs, the repository may be rolled back to a partial
1852 if an error occurs, the repository may be rolled back to a partial
1853 clone. This behavior may change in future releases.
1853 clone. This behavior may change in future releases.
1854 See :hg:`help -e clonebundles` for more.
1854 See :hg:`help -e clonebundles` for more.
1855
1855
1856 Examples:
1856 Examples:
1857
1857
1858 - clone a remote repository to a new directory named hg/::
1858 - clone a remote repository to a new directory named hg/::
1859
1859
1860 hg clone https://www.mercurial-scm.org/repo/hg/
1860 hg clone https://www.mercurial-scm.org/repo/hg/
1861
1861
1862 - create a lightweight local clone::
1862 - create a lightweight local clone::
1863
1863
1864 hg clone project/ project-feature/
1864 hg clone project/ project-feature/
1865
1865
1866 - clone from an absolute path on an ssh server (note double-slash)::
1866 - clone from an absolute path on an ssh server (note double-slash)::
1867
1867
1868 hg clone ssh://user@server//home/projects/alpha/
1868 hg clone ssh://user@server//home/projects/alpha/
1869
1869
1870 - do a streaming clone while checking out a specified version::
1870 - do a streaming clone while checking out a specified version::
1871
1871
1872 hg clone --stream http://server/repo -u 1.5
1872 hg clone --stream http://server/repo -u 1.5
1873
1873
1874 - create a repository without changesets after a particular revision::
1874 - create a repository without changesets after a particular revision::
1875
1875
1876 hg clone -r 04e544 experimental/ good/
1876 hg clone -r 04e544 experimental/ good/
1877
1877
1878 - clone (and track) a particular named branch::
1878 - clone (and track) a particular named branch::
1879
1879
1880 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1880 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1881
1881
1882 See :hg:`help urls` for details on specifying URLs.
1882 See :hg:`help urls` for details on specifying URLs.
1883
1883
1884 Returns 0 on success.
1884 Returns 0 on success.
1885 """
1885 """
1886 opts = pycompat.byteskwargs(opts)
1886 opts = pycompat.byteskwargs(opts)
1887 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1887 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1888
1888
1889 # --include/--exclude can come from narrow or sparse.
1889 # --include/--exclude can come from narrow or sparse.
1890 includepats, excludepats = None, None
1890 includepats, excludepats = None, None
1891
1891
1892 # hg.clone() differentiates between None and an empty set. So make sure
1892 # hg.clone() differentiates between None and an empty set. So make sure
1893 # patterns are sets if narrow is requested without patterns.
1893 # patterns are sets if narrow is requested without patterns.
1894 if opts.get(b'narrow'):
1894 if opts.get(b'narrow'):
1895 includepats = set()
1895 includepats = set()
1896 excludepats = set()
1896 excludepats = set()
1897
1897
1898 if opts.get(b'include'):
1898 if opts.get(b'include'):
1899 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1899 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1900 if opts.get(b'exclude'):
1900 if opts.get(b'exclude'):
1901 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1901 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1902
1902
1903 r = hg.clone(
1903 r = hg.clone(
1904 ui,
1904 ui,
1905 opts,
1905 opts,
1906 source,
1906 source,
1907 dest,
1907 dest,
1908 pull=opts.get(b'pull'),
1908 pull=opts.get(b'pull'),
1909 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1909 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1910 revs=opts.get(b'rev'),
1910 revs=opts.get(b'rev'),
1911 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1911 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1912 branch=opts.get(b'branch'),
1912 branch=opts.get(b'branch'),
1913 shareopts=opts.get(b'shareopts'),
1913 shareopts=opts.get(b'shareopts'),
1914 storeincludepats=includepats,
1914 storeincludepats=includepats,
1915 storeexcludepats=excludepats,
1915 storeexcludepats=excludepats,
1916 depth=opts.get(b'depth') or None,
1916 depth=opts.get(b'depth') or None,
1917 )
1917 )
1918
1918
1919 return r is None
1919 return r is None
1920
1920
1921
1921
1922 @command(
1922 @command(
1923 b'commit|ci',
1923 b'commit|ci',
1924 [
1924 [
1925 (
1925 (
1926 b'A',
1926 b'A',
1927 b'addremove',
1927 b'addremove',
1928 None,
1928 None,
1929 _(b'mark new/missing files as added/removed before committing'),
1929 _(b'mark new/missing files as added/removed before committing'),
1930 ),
1930 ),
1931 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1931 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1932 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1932 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1933 (b's', b'secret', None, _(b'use the secret phase for committing')),
1933 (b's', b'secret', None, _(b'use the secret phase for committing')),
1934 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1934 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1935 (
1935 (
1936 b'',
1936 b'',
1937 b'force-close-branch',
1937 b'force-close-branch',
1938 None,
1938 None,
1939 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1939 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1940 ),
1940 ),
1941 (b'i', b'interactive', None, _(b'use interactive mode')),
1941 (b'i', b'interactive', None, _(b'use interactive mode')),
1942 ]
1942 ]
1943 + walkopts
1943 + walkopts
1944 + commitopts
1944 + commitopts
1945 + commitopts2
1945 + commitopts2
1946 + subrepoopts,
1946 + subrepoopts,
1947 _(b'[OPTION]... [FILE]...'),
1947 _(b'[OPTION]... [FILE]...'),
1948 helpcategory=command.CATEGORY_COMMITTING,
1948 helpcategory=command.CATEGORY_COMMITTING,
1949 helpbasic=True,
1949 helpbasic=True,
1950 inferrepo=True,
1950 inferrepo=True,
1951 )
1951 )
1952 def commit(ui, repo, *pats, **opts):
1952 def commit(ui, repo, *pats, **opts):
1953 """commit the specified files or all outstanding changes
1953 """commit the specified files or all outstanding changes
1954
1954
1955 Commit changes to the given files into the repository. Unlike a
1955 Commit changes to the given files into the repository. Unlike a
1956 centralized SCM, this operation is a local operation. See
1956 centralized SCM, this operation is a local operation. See
1957 :hg:`push` for a way to actively distribute your changes.
1957 :hg:`push` for a way to actively distribute your changes.
1958
1958
1959 If a list of files is omitted, all changes reported by :hg:`status`
1959 If a list of files is omitted, all changes reported by :hg:`status`
1960 will be committed.
1960 will be committed.
1961
1961
1962 If you are committing the result of a merge, do not provide any
1962 If you are committing the result of a merge, do not provide any
1963 filenames or -I/-X filters.
1963 filenames or -I/-X filters.
1964
1964
1965 If no commit message is specified, Mercurial starts your
1965 If no commit message is specified, Mercurial starts your
1966 configured editor where you can enter a message. In case your
1966 configured editor where you can enter a message. In case your
1967 commit fails, you will find a backup of your message in
1967 commit fails, you will find a backup of your message in
1968 ``.hg/last-message.txt``.
1968 ``.hg/last-message.txt``.
1969
1969
1970 The --close-branch flag can be used to mark the current branch
1970 The --close-branch flag can be used to mark the current branch
1971 head closed. When all heads of a branch are closed, the branch
1971 head closed. When all heads of a branch are closed, the branch
1972 will be considered closed and no longer listed.
1972 will be considered closed and no longer listed.
1973
1973
1974 The --amend flag can be used to amend the parent of the
1974 The --amend flag can be used to amend the parent of the
1975 working directory with a new commit that contains the changes
1975 working directory with a new commit that contains the changes
1976 in the parent in addition to those currently reported by :hg:`status`,
1976 in the parent in addition to those currently reported by :hg:`status`,
1977 if there are any. The old commit is stored in a backup bundle in
1977 if there are any. The old commit is stored in a backup bundle in
1978 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1978 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1979 on how to restore it).
1979 on how to restore it).
1980
1980
1981 Message, user and date are taken from the amended commit unless
1981 Message, user and date are taken from the amended commit unless
1982 specified. When a message isn't specified on the command line,
1982 specified. When a message isn't specified on the command line,
1983 the editor will open with the message of the amended commit.
1983 the editor will open with the message of the amended commit.
1984
1984
1985 It is not possible to amend public changesets (see :hg:`help phases`)
1985 It is not possible to amend public changesets (see :hg:`help phases`)
1986 or changesets that have children.
1986 or changesets that have children.
1987
1987
1988 See :hg:`help dates` for a list of formats valid for -d/--date.
1988 See :hg:`help dates` for a list of formats valid for -d/--date.
1989
1989
1990 Returns 0 on success, 1 if nothing changed.
1990 Returns 0 on success, 1 if nothing changed.
1991
1991
1992 .. container:: verbose
1992 .. container:: verbose
1993
1993
1994 Examples:
1994 Examples:
1995
1995
1996 - commit all files ending in .py::
1996 - commit all files ending in .py::
1997
1997
1998 hg commit --include "set:**.py"
1998 hg commit --include "set:**.py"
1999
1999
2000 - commit all non-binary files::
2000 - commit all non-binary files::
2001
2001
2002 hg commit --exclude "set:binary()"
2002 hg commit --exclude "set:binary()"
2003
2003
2004 - amend the current commit and set the date to now::
2004 - amend the current commit and set the date to now::
2005
2005
2006 hg commit --amend --date now
2006 hg commit --amend --date now
2007 """
2007 """
2008 with repo.wlock(), repo.lock():
2008 with repo.wlock(), repo.lock():
2009 return _docommit(ui, repo, *pats, **opts)
2009 return _docommit(ui, repo, *pats, **opts)
2010
2010
2011
2011
2012 def _docommit(ui, repo, *pats, **opts):
2012 def _docommit(ui, repo, *pats, **opts):
2013 if opts.get('interactive'):
2013 if opts.get('interactive'):
2014 opts.pop('interactive')
2014 opts.pop('interactive')
2015 ret = cmdutil.dorecord(
2015 ret = cmdutil.dorecord(
2016 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2016 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2017 )
2017 )
2018 # ret can be 0 (no changes to record) or the value returned by
2018 # ret can be 0 (no changes to record) or the value returned by
2019 # commit(), 1 if nothing changed or None on success.
2019 # commit(), 1 if nothing changed or None on success.
2020 return 1 if ret == 0 else ret
2020 return 1 if ret == 0 else ret
2021
2021
2022 opts = pycompat.byteskwargs(opts)
2022 opts = pycompat.byteskwargs(opts)
2023 if opts.get(b'subrepos'):
2023 if opts.get(b'subrepos'):
2024 if opts.get(b'amend'):
2024 if opts.get(b'amend'):
2025 raise error.Abort(_(b'cannot amend with --subrepos'))
2025 raise error.Abort(_(b'cannot amend with --subrepos'))
2026 # Let --subrepos on the command line override config setting.
2026 # Let --subrepos on the command line override config setting.
2027 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2027 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2028
2028
2029 cmdutil.checkunfinished(repo, commit=True)
2029 cmdutil.checkunfinished(repo, commit=True)
2030
2030
2031 branch = repo[None].branch()
2031 branch = repo[None].branch()
2032 bheads = repo.branchheads(branch)
2032 bheads = repo.branchheads(branch)
2033
2033
2034 extra = {}
2034 extra = {}
2035 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2035 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2036 extra[b'close'] = b'1'
2036 extra[b'close'] = b'1'
2037
2037
2038 if repo[b'.'].closesbranch():
2038 if repo[b'.'].closesbranch():
2039 raise error.Abort(
2039 raise error.Abort(
2040 _(b'current revision is already a branch closing head')
2040 _(b'current revision is already a branch closing head')
2041 )
2041 )
2042 elif not bheads:
2042 elif not bheads:
2043 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2043 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2044 elif (
2044 elif (
2045 branch == repo[b'.'].branch()
2045 branch == repo[b'.'].branch()
2046 and repo[b'.'].node() not in bheads
2046 and repo[b'.'].node() not in bheads
2047 and not opts.get(b'force_close_branch')
2047 and not opts.get(b'force_close_branch')
2048 ):
2048 ):
2049 hint = _(
2049 hint = _(
2050 b'use --force-close-branch to close branch from a non-head'
2050 b'use --force-close-branch to close branch from a non-head'
2051 b' changeset'
2051 b' changeset'
2052 )
2052 )
2053 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2053 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2054 elif opts.get(b'amend'):
2054 elif opts.get(b'amend'):
2055 if (
2055 if (
2056 repo[b'.'].p1().branch() != branch
2056 repo[b'.'].p1().branch() != branch
2057 and repo[b'.'].p2().branch() != branch
2057 and repo[b'.'].p2().branch() != branch
2058 ):
2058 ):
2059 raise error.Abort(_(b'can only close branch heads'))
2059 raise error.Abort(_(b'can only close branch heads'))
2060
2060
2061 if opts.get(b'amend'):
2061 if opts.get(b'amend'):
2062 if ui.configbool(b'ui', b'commitsubrepos'):
2062 if ui.configbool(b'ui', b'commitsubrepos'):
2063 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2063 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2064
2064
2065 old = repo[b'.']
2065 old = repo[b'.']
2066 rewriteutil.precheck(repo, [old.rev()], b'amend')
2066 rewriteutil.precheck(repo, [old.rev()], b'amend')
2067
2067
2068 # Currently histedit gets confused if an amend happens while histedit
2068 # Currently histedit gets confused if an amend happens while histedit
2069 # is in progress. Since we have a checkunfinished command, we are
2069 # is in progress. Since we have a checkunfinished command, we are
2070 # temporarily honoring it.
2070 # temporarily honoring it.
2071 #
2071 #
2072 # Note: eventually this guard will be removed. Please do not expect
2072 # Note: eventually this guard will be removed. Please do not expect
2073 # this behavior to remain.
2073 # this behavior to remain.
2074 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2074 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2075 cmdutil.checkunfinished(repo)
2075 cmdutil.checkunfinished(repo)
2076
2076
2077 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2077 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2078 if node == old.node():
2078 if node == old.node():
2079 ui.status(_(b"nothing changed\n"))
2079 ui.status(_(b"nothing changed\n"))
2080 return 1
2080 return 1
2081 else:
2081 else:
2082
2082
2083 def commitfunc(ui, repo, message, match, opts):
2083 def commitfunc(ui, repo, message, match, opts):
2084 overrides = {}
2084 overrides = {}
2085 if opts.get(b'secret'):
2085 if opts.get(b'secret'):
2086 overrides[(b'phases', b'new-commit')] = b'secret'
2086 overrides[(b'phases', b'new-commit')] = b'secret'
2087
2087
2088 baseui = repo.baseui
2088 baseui = repo.baseui
2089 with baseui.configoverride(overrides, b'commit'):
2089 with baseui.configoverride(overrides, b'commit'):
2090 with ui.configoverride(overrides, b'commit'):
2090 with ui.configoverride(overrides, b'commit'):
2091 editform = cmdutil.mergeeditform(
2091 editform = cmdutil.mergeeditform(
2092 repo[None], b'commit.normal'
2092 repo[None], b'commit.normal'
2093 )
2093 )
2094 editor = cmdutil.getcommiteditor(
2094 editor = cmdutil.getcommiteditor(
2095 editform=editform, **pycompat.strkwargs(opts)
2095 editform=editform, **pycompat.strkwargs(opts)
2096 )
2096 )
2097 return repo.commit(
2097 return repo.commit(
2098 message,
2098 message,
2099 opts.get(b'user'),
2099 opts.get(b'user'),
2100 opts.get(b'date'),
2100 opts.get(b'date'),
2101 match,
2101 match,
2102 editor=editor,
2102 editor=editor,
2103 extra=extra,
2103 extra=extra,
2104 )
2104 )
2105
2105
2106 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2106 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2107
2107
2108 if not node:
2108 if not node:
2109 stat = cmdutil.postcommitstatus(repo, pats, opts)
2109 stat = cmdutil.postcommitstatus(repo, pats, opts)
2110 if stat.deleted:
2110 if stat.deleted:
2111 ui.status(
2111 ui.status(
2112 _(
2112 _(
2113 b"nothing changed (%d missing files, see "
2113 b"nothing changed (%d missing files, see "
2114 b"'hg status')\n"
2114 b"'hg status')\n"
2115 )
2115 )
2116 % len(stat.deleted)
2116 % len(stat.deleted)
2117 )
2117 )
2118 else:
2118 else:
2119 ui.status(_(b"nothing changed\n"))
2119 ui.status(_(b"nothing changed\n"))
2120 return 1
2120 return 1
2121
2121
2122 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2122 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2123
2123
2124 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2124 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2125 status(
2125 status(
2126 ui,
2126 ui,
2127 repo,
2127 repo,
2128 modified=True,
2128 modified=True,
2129 added=True,
2129 added=True,
2130 removed=True,
2130 removed=True,
2131 deleted=True,
2131 deleted=True,
2132 unknown=True,
2132 unknown=True,
2133 subrepos=opts.get(b'subrepos'),
2133 subrepos=opts.get(b'subrepos'),
2134 )
2134 )
2135
2135
2136
2136
2137 @command(
2137 @command(
2138 b'config|showconfig|debugconfig',
2138 b'config|showconfig|debugconfig',
2139 [
2139 [
2140 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2140 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2141 (b'e', b'edit', None, _(b'edit user config')),
2141 (b'e', b'edit', None, _(b'edit user config')),
2142 (b'l', b'local', None, _(b'edit repository config')),
2142 (b'l', b'local', None, _(b'edit repository config')),
2143 (b'g', b'global', None, _(b'edit global config')),
2143 (b'g', b'global', None, _(b'edit global config')),
2144 ]
2144 ]
2145 + formatteropts,
2145 + formatteropts,
2146 _(b'[-u] [NAME]...'),
2146 _(b'[-u] [NAME]...'),
2147 helpcategory=command.CATEGORY_HELP,
2147 helpcategory=command.CATEGORY_HELP,
2148 optionalrepo=True,
2148 optionalrepo=True,
2149 intents={INTENT_READONLY},
2149 intents={INTENT_READONLY},
2150 )
2150 )
2151 def config(ui, repo, *values, **opts):
2151 def config(ui, repo, *values, **opts):
2152 """show combined config settings from all hgrc files
2152 """show combined config settings from all hgrc files
2153
2153
2154 With no arguments, print names and values of all config items.
2154 With no arguments, print names and values of all config items.
2155
2155
2156 With one argument of the form section.name, print just the value
2156 With one argument of the form section.name, print just the value
2157 of that config item.
2157 of that config item.
2158
2158
2159 With multiple arguments, print names and values of all config
2159 With multiple arguments, print names and values of all config
2160 items with matching section names or section.names.
2160 items with matching section names or section.names.
2161
2161
2162 With --edit, start an editor on the user-level config file. With
2162 With --edit, start an editor on the user-level config file. With
2163 --global, edit the system-wide config file. With --local, edit the
2163 --global, edit the system-wide config file. With --local, edit the
2164 repository-level config file.
2164 repository-level config file.
2165
2165
2166 With --debug, the source (filename and line number) is printed
2166 With --debug, the source (filename and line number) is printed
2167 for each config item.
2167 for each config item.
2168
2168
2169 See :hg:`help config` for more information about config files.
2169 See :hg:`help config` for more information about config files.
2170
2170
2171 .. container:: verbose
2171 .. container:: verbose
2172
2172
2173 Template:
2173 Template:
2174
2174
2175 The following keywords are supported. See also :hg:`help templates`.
2175 The following keywords are supported. See also :hg:`help templates`.
2176
2176
2177 :name: String. Config name.
2177 :name: String. Config name.
2178 :source: String. Filename and line number where the item is defined.
2178 :source: String. Filename and line number where the item is defined.
2179 :value: String. Config value.
2179 :value: String. Config value.
2180
2180
2181 Returns 0 on success, 1 if NAME does not exist.
2181 Returns 0 on success, 1 if NAME does not exist.
2182
2182
2183 """
2183 """
2184
2184
2185 opts = pycompat.byteskwargs(opts)
2185 opts = pycompat.byteskwargs(opts)
2186 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2186 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2187 if opts.get(b'local') and opts.get(b'global'):
2187 if opts.get(b'local') and opts.get(b'global'):
2188 raise error.Abort(_(b"can't use --local and --global together"))
2188 raise error.Abort(_(b"can't use --local and --global together"))
2189
2189
2190 if opts.get(b'local'):
2190 if opts.get(b'local'):
2191 if not repo:
2191 if not repo:
2192 raise error.Abort(_(b"can't use --local outside a repository"))
2192 raise error.Abort(_(b"can't use --local outside a repository"))
2193 paths = [repo.vfs.join(b'hgrc')]
2193 paths = [repo.vfs.join(b'hgrc')]
2194 elif opts.get(b'global'):
2194 elif opts.get(b'global'):
2195 paths = rcutil.systemrcpath()
2195 paths = rcutil.systemrcpath()
2196 else:
2196 else:
2197 paths = rcutil.userrcpath()
2197 paths = rcutil.userrcpath()
2198
2198
2199 for f in paths:
2199 for f in paths:
2200 if os.path.exists(f):
2200 if os.path.exists(f):
2201 break
2201 break
2202 else:
2202 else:
2203 if opts.get(b'global'):
2203 if opts.get(b'global'):
2204 samplehgrc = uimod.samplehgrcs[b'global']
2204 samplehgrc = uimod.samplehgrcs[b'global']
2205 elif opts.get(b'local'):
2205 elif opts.get(b'local'):
2206 samplehgrc = uimod.samplehgrcs[b'local']
2206 samplehgrc = uimod.samplehgrcs[b'local']
2207 else:
2207 else:
2208 samplehgrc = uimod.samplehgrcs[b'user']
2208 samplehgrc = uimod.samplehgrcs[b'user']
2209
2209
2210 f = paths[0]
2210 f = paths[0]
2211 fp = open(f, b"wb")
2211 fp = open(f, b"wb")
2212 fp.write(util.tonativeeol(samplehgrc))
2212 fp.write(util.tonativeeol(samplehgrc))
2213 fp.close()
2213 fp.close()
2214
2214
2215 editor = ui.geteditor()
2215 editor = ui.geteditor()
2216 ui.system(
2216 ui.system(
2217 b"%s \"%s\"" % (editor, f),
2217 b"%s \"%s\"" % (editor, f),
2218 onerr=error.Abort,
2218 onerr=error.Abort,
2219 errprefix=_(b"edit failed"),
2219 errprefix=_(b"edit failed"),
2220 blockedtag=b'config_edit',
2220 blockedtag=b'config_edit',
2221 )
2221 )
2222 return
2222 return
2223 ui.pager(b'config')
2223 ui.pager(b'config')
2224 fm = ui.formatter(b'config', opts)
2224 fm = ui.formatter(b'config', opts)
2225 for t, f in rcutil.rccomponents():
2225 for t, f in rcutil.rccomponents():
2226 if t == b'path':
2226 if t == b'path':
2227 ui.debug(b'read config from: %s\n' % f)
2227 ui.debug(b'read config from: %s\n' % f)
2228 elif t == b'resource':
2228 elif t == b'resource':
2229 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2229 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2230 elif t == b'items':
2230 elif t == b'items':
2231 # Don't print anything for 'items'.
2231 # Don't print anything for 'items'.
2232 pass
2232 pass
2233 else:
2233 else:
2234 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2234 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2235 untrusted = bool(opts.get(b'untrusted'))
2235 untrusted = bool(opts.get(b'untrusted'))
2236
2236
2237 selsections = selentries = []
2237 selsections = selentries = []
2238 if values:
2238 if values:
2239 selsections = [v for v in values if b'.' not in v]
2239 selsections = [v for v in values if b'.' not in v]
2240 selentries = [v for v in values if b'.' in v]
2240 selentries = [v for v in values if b'.' in v]
2241 uniquesel = len(selentries) == 1 and not selsections
2241 uniquesel = len(selentries) == 1 and not selsections
2242 selsections = set(selsections)
2242 selsections = set(selsections)
2243 selentries = set(selentries)
2243 selentries = set(selentries)
2244
2244
2245 matched = False
2245 matched = False
2246 for section, name, value in ui.walkconfig(untrusted=untrusted):
2246 for section, name, value in ui.walkconfig(untrusted=untrusted):
2247 source = ui.configsource(section, name, untrusted)
2247 source = ui.configsource(section, name, untrusted)
2248 value = pycompat.bytestr(value)
2248 value = pycompat.bytestr(value)
2249 defaultvalue = ui.configdefault(section, name)
2249 defaultvalue = ui.configdefault(section, name)
2250 if fm.isplain():
2250 if fm.isplain():
2251 source = source or b'none'
2251 source = source or b'none'
2252 value = value.replace(b'\n', b'\\n')
2252 value = value.replace(b'\n', b'\\n')
2253 entryname = section + b'.' + name
2253 entryname = section + b'.' + name
2254 if values and not (section in selsections or entryname in selentries):
2254 if values and not (section in selsections or entryname in selentries):
2255 continue
2255 continue
2256 fm.startitem()
2256 fm.startitem()
2257 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2257 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2258 if uniquesel:
2258 if uniquesel:
2259 fm.data(name=entryname)
2259 fm.data(name=entryname)
2260 fm.write(b'value', b'%s\n', value)
2260 fm.write(b'value', b'%s\n', value)
2261 else:
2261 else:
2262 fm.write(b'name value', b'%s=%s\n', entryname, value)
2262 fm.write(b'name value', b'%s=%s\n', entryname, value)
2263 if formatter.isprintable(defaultvalue):
2263 if formatter.isprintable(defaultvalue):
2264 fm.data(defaultvalue=defaultvalue)
2264 fm.data(defaultvalue=defaultvalue)
2265 elif isinstance(defaultvalue, list) and all(
2265 elif isinstance(defaultvalue, list) and all(
2266 formatter.isprintable(e) for e in defaultvalue
2266 formatter.isprintable(e) for e in defaultvalue
2267 ):
2267 ):
2268 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2268 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2269 # TODO: no idea how to process unsupported defaultvalue types
2269 # TODO: no idea how to process unsupported defaultvalue types
2270 matched = True
2270 matched = True
2271 fm.end()
2271 fm.end()
2272 if matched:
2272 if matched:
2273 return 0
2273 return 0
2274 return 1
2274 return 1
2275
2275
2276
2276
2277 @command(
2277 @command(
2278 b'continue',
2278 b'continue',
2279 dryrunopts,
2279 dryrunopts,
2280 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2280 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2281 helpbasic=True,
2281 helpbasic=True,
2282 )
2282 )
2283 def continuecmd(ui, repo, **opts):
2283 def continuecmd(ui, repo, **opts):
2284 """resumes an interrupted operation (EXPERIMENTAL)
2284 """resumes an interrupted operation (EXPERIMENTAL)
2285
2285
2286 Finishes a multistep operation like graft, histedit, rebase, merge,
2286 Finishes a multistep operation like graft, histedit, rebase, merge,
2287 and unshelve if they are in an interrupted state.
2287 and unshelve if they are in an interrupted state.
2288
2288
2289 use --dry-run/-n to dry run the command.
2289 use --dry-run/-n to dry run the command.
2290 """
2290 """
2291 dryrun = opts.get('dry_run')
2291 dryrun = opts.get('dry_run')
2292 contstate = cmdutil.getunfinishedstate(repo)
2292 contstate = cmdutil.getunfinishedstate(repo)
2293 if not contstate:
2293 if not contstate:
2294 raise error.Abort(_(b'no operation in progress'))
2294 raise error.Abort(_(b'no operation in progress'))
2295 if not contstate.continuefunc:
2295 if not contstate.continuefunc:
2296 raise error.Abort(
2296 raise error.Abort(
2297 (
2297 (
2298 _(b"%s in progress but does not support 'hg continue'")
2298 _(b"%s in progress but does not support 'hg continue'")
2299 % (contstate._opname)
2299 % (contstate._opname)
2300 ),
2300 ),
2301 hint=contstate.continuemsg(),
2301 hint=contstate.continuemsg(),
2302 )
2302 )
2303 if dryrun:
2303 if dryrun:
2304 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2304 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2305 return
2305 return
2306 return contstate.continuefunc(ui, repo)
2306 return contstate.continuefunc(ui, repo)
2307
2307
2308
2308
2309 @command(
2309 @command(
2310 b'copy|cp',
2310 b'copy|cp',
2311 [
2311 [
2312 (b'', b'forget', None, _(b'unmark a file as copied')),
2312 (b'', b'forget', None, _(b'unmark a file as copied')),
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2314 (
2314 (
2315 b'',
2315 b'',
2316 b'at-rev',
2316 b'at-rev',
2317 b'',
2317 b'',
2318 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2318 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2319 _(b'REV'),
2319 _(b'REV'),
2320 ),
2320 ),
2321 (
2321 (
2322 b'f',
2322 b'f',
2323 b'force',
2323 b'force',
2324 None,
2324 None,
2325 _(b'forcibly copy over an existing managed file'),
2325 _(b'forcibly copy over an existing managed file'),
2326 ),
2326 ),
2327 ]
2327 ]
2328 + walkopts
2328 + walkopts
2329 + dryrunopts,
2329 + dryrunopts,
2330 _(b'[OPTION]... SOURCE... DEST'),
2330 _(b'[OPTION]... SOURCE... DEST'),
2331 helpcategory=command.CATEGORY_FILE_CONTENTS,
2331 helpcategory=command.CATEGORY_FILE_CONTENTS,
2332 )
2332 )
2333 def copy(ui, repo, *pats, **opts):
2333 def copy(ui, repo, *pats, **opts):
2334 """mark files as copied for the next commit
2334 """mark files as copied for the next commit
2335
2335
2336 Mark dest as having copies of source files. If dest is a
2336 Mark dest as having copies of source files. If dest is a
2337 directory, copies are put in that directory. If dest is a file,
2337 directory, copies are put in that directory. If dest is a file,
2338 the source must be a single file.
2338 the source must be a single file.
2339
2339
2340 By default, this command copies the contents of files as they
2340 By default, this command copies the contents of files as they
2341 exist in the working directory. If invoked with -A/--after, the
2341 exist in the working directory. If invoked with -A/--after, the
2342 operation is recorded, but no copying is performed.
2342 operation is recorded, but no copying is performed.
2343
2343
2344 To undo marking a file as copied, use --forget. With that option,
2344 To undo marking a file as copied, use --forget. With that option,
2345 all given (positional) arguments are unmarked as copies. The destination
2345 all given (positional) arguments are unmarked as copies. The destination
2346 file(s) will be left in place (still tracked).
2346 file(s) will be left in place (still tracked).
2347
2347
2348 This command takes effect with the next commit by default.
2348 This command takes effect with the next commit by default.
2349
2349
2350 Returns 0 on success, 1 if errors are encountered.
2350 Returns 0 on success, 1 if errors are encountered.
2351 """
2351 """
2352 opts = pycompat.byteskwargs(opts)
2352 opts = pycompat.byteskwargs(opts)
2353 with repo.wlock(False):
2353 with repo.wlock(False):
2354 return cmdutil.copy(ui, repo, pats, opts)
2354 return cmdutil.copy(ui, repo, pats, opts)
2355
2355
2356
2356
2357 @command(
2357 @command(
2358 b'debugcommands',
2358 b'debugcommands',
2359 [],
2359 [],
2360 _(b'[COMMAND]'),
2360 _(b'[COMMAND]'),
2361 helpcategory=command.CATEGORY_HELP,
2361 helpcategory=command.CATEGORY_HELP,
2362 norepo=True,
2362 norepo=True,
2363 )
2363 )
2364 def debugcommands(ui, cmd=b'', *args):
2364 def debugcommands(ui, cmd=b'', *args):
2365 """list all available commands and options"""
2365 """list all available commands and options"""
2366 for cmd, vals in sorted(pycompat.iteritems(table)):
2366 for cmd, vals in sorted(pycompat.iteritems(table)):
2367 cmd = cmd.split(b'|')[0]
2367 cmd = cmd.split(b'|')[0]
2368 opts = b', '.join([i[1] for i in vals[1]])
2368 opts = b', '.join([i[1] for i in vals[1]])
2369 ui.write(b'%s: %s\n' % (cmd, opts))
2369 ui.write(b'%s: %s\n' % (cmd, opts))
2370
2370
2371
2371
2372 @command(
2372 @command(
2373 b'debugcomplete',
2373 b'debugcomplete',
2374 [(b'o', b'options', None, _(b'show the command options'))],
2374 [(b'o', b'options', None, _(b'show the command options'))],
2375 _(b'[-o] CMD'),
2375 _(b'[-o] CMD'),
2376 helpcategory=command.CATEGORY_HELP,
2376 helpcategory=command.CATEGORY_HELP,
2377 norepo=True,
2377 norepo=True,
2378 )
2378 )
2379 def debugcomplete(ui, cmd=b'', **opts):
2379 def debugcomplete(ui, cmd=b'', **opts):
2380 """returns the completion list associated with the given command"""
2380 """returns the completion list associated with the given command"""
2381
2381
2382 if opts.get('options'):
2382 if opts.get('options'):
2383 options = []
2383 options = []
2384 otables = [globalopts]
2384 otables = [globalopts]
2385 if cmd:
2385 if cmd:
2386 aliases, entry = cmdutil.findcmd(cmd, table, False)
2386 aliases, entry = cmdutil.findcmd(cmd, table, False)
2387 otables.append(entry[1])
2387 otables.append(entry[1])
2388 for t in otables:
2388 for t in otables:
2389 for o in t:
2389 for o in t:
2390 if b"(DEPRECATED)" in o[3]:
2390 if b"(DEPRECATED)" in o[3]:
2391 continue
2391 continue
2392 if o[0]:
2392 if o[0]:
2393 options.append(b'-%s' % o[0])
2393 options.append(b'-%s' % o[0])
2394 options.append(b'--%s' % o[1])
2394 options.append(b'--%s' % o[1])
2395 ui.write(b"%s\n" % b"\n".join(options))
2395 ui.write(b"%s\n" % b"\n".join(options))
2396 return
2396 return
2397
2397
2398 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2398 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2399 if ui.verbose:
2399 if ui.verbose:
2400 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2400 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2401 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2401 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2402
2402
2403
2403
2404 @command(
2404 @command(
2405 b'diff',
2405 b'diff',
2406 [
2406 [
2407 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2407 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2408 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2408 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2409 ]
2409 ]
2410 + diffopts
2410 + diffopts
2411 + diffopts2
2411 + diffopts2
2412 + walkopts
2412 + walkopts
2413 + subrepoopts,
2413 + subrepoopts,
2414 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2414 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2415 helpcategory=command.CATEGORY_FILE_CONTENTS,
2415 helpcategory=command.CATEGORY_FILE_CONTENTS,
2416 helpbasic=True,
2416 helpbasic=True,
2417 inferrepo=True,
2417 inferrepo=True,
2418 intents={INTENT_READONLY},
2418 intents={INTENT_READONLY},
2419 )
2419 )
2420 def diff(ui, repo, *pats, **opts):
2420 def diff(ui, repo, *pats, **opts):
2421 """diff repository (or selected files)
2421 """diff repository (or selected files)
2422
2422
2423 Show differences between revisions for the specified files.
2423 Show differences between revisions for the specified files.
2424
2424
2425 Differences between files are shown using the unified diff format.
2425 Differences between files are shown using the unified diff format.
2426
2426
2427 .. note::
2427 .. note::
2428
2428
2429 :hg:`diff` may generate unexpected results for merges, as it will
2429 :hg:`diff` may generate unexpected results for merges, as it will
2430 default to comparing against the working directory's first
2430 default to comparing against the working directory's first
2431 parent changeset if no revisions are specified.
2431 parent changeset if no revisions are specified.
2432
2432
2433 When two revision arguments are given, then changes are shown
2433 When two revision arguments are given, then changes are shown
2434 between those revisions. If only one revision is specified then
2434 between those revisions. If only one revision is specified then
2435 that revision is compared to the working directory, and, when no
2435 that revision is compared to the working directory, and, when no
2436 revisions are specified, the working directory files are compared
2436 revisions are specified, the working directory files are compared
2437 to its first parent.
2437 to its first parent.
2438
2438
2439 Alternatively you can specify -c/--change with a revision to see
2439 Alternatively you can specify -c/--change with a revision to see
2440 the changes in that changeset relative to its first parent.
2440 the changes in that changeset relative to its first parent.
2441
2441
2442 Without the -a/--text option, diff will avoid generating diffs of
2442 Without the -a/--text option, diff will avoid generating diffs of
2443 files it detects as binary. With -a, diff will generate a diff
2443 files it detects as binary. With -a, diff will generate a diff
2444 anyway, probably with undesirable results.
2444 anyway, probably with undesirable results.
2445
2445
2446 Use the -g/--git option to generate diffs in the git extended diff
2446 Use the -g/--git option to generate diffs in the git extended diff
2447 format. For more information, read :hg:`help diffs`.
2447 format. For more information, read :hg:`help diffs`.
2448
2448
2449 .. container:: verbose
2449 .. container:: verbose
2450
2450
2451 Examples:
2451 Examples:
2452
2452
2453 - compare a file in the current working directory to its parent::
2453 - compare a file in the current working directory to its parent::
2454
2454
2455 hg diff foo.c
2455 hg diff foo.c
2456
2456
2457 - compare two historical versions of a directory, with rename info::
2457 - compare two historical versions of a directory, with rename info::
2458
2458
2459 hg diff --git -r 1.0:1.2 lib/
2459 hg diff --git -r 1.0:1.2 lib/
2460
2460
2461 - get change stats relative to the last change on some date::
2461 - get change stats relative to the last change on some date::
2462
2462
2463 hg diff --stat -r "date('may 2')"
2463 hg diff --stat -r "date('may 2')"
2464
2464
2465 - diff all newly-added files that contain a keyword::
2465 - diff all newly-added files that contain a keyword::
2466
2466
2467 hg diff "set:added() and grep(GNU)"
2467 hg diff "set:added() and grep(GNU)"
2468
2468
2469 - compare a revision and its parents::
2469 - compare a revision and its parents::
2470
2470
2471 hg diff -c 9353 # compare against first parent
2471 hg diff -c 9353 # compare against first parent
2472 hg diff -r 9353^:9353 # same using revset syntax
2472 hg diff -r 9353^:9353 # same using revset syntax
2473 hg diff -r 9353^2:9353 # compare against the second parent
2473 hg diff -r 9353^2:9353 # compare against the second parent
2474
2474
2475 Returns 0 on success.
2475 Returns 0 on success.
2476 """
2476 """
2477
2477
2478 opts = pycompat.byteskwargs(opts)
2478 opts = pycompat.byteskwargs(opts)
2479 revs = opts.get(b'rev')
2479 revs = opts.get(b'rev')
2480 change = opts.get(b'change')
2480 change = opts.get(b'change')
2481 stat = opts.get(b'stat')
2481 stat = opts.get(b'stat')
2482 reverse = opts.get(b'reverse')
2482 reverse = opts.get(b'reverse')
2483
2483
2484 if revs and change:
2484 if revs and change:
2485 msg = _(b'cannot specify --rev and --change at the same time')
2485 msg = _(b'cannot specify --rev and --change at the same time')
2486 raise error.Abort(msg)
2486 raise error.Abort(msg)
2487 elif change:
2487 elif change:
2488 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2488 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2489 ctx2 = scmutil.revsingle(repo, change, None)
2489 ctx2 = scmutil.revsingle(repo, change, None)
2490 ctx1 = ctx2.p1()
2490 ctx1 = ctx2.p1()
2491 else:
2491 else:
2492 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2492 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2493 ctx1, ctx2 = scmutil.revpair(repo, revs)
2493 ctx1, ctx2 = scmutil.revpair(repo, revs)
2494 node1, node2 = ctx1.node(), ctx2.node()
2494 node1, node2 = ctx1.node(), ctx2.node()
2495
2495
2496 if reverse:
2496 if reverse:
2497 node1, node2 = node2, node1
2497 node1, node2 = node2, node1
2498
2498
2499 diffopts = patch.diffallopts(ui, opts)
2499 diffopts = patch.diffallopts(ui, opts)
2500 m = scmutil.match(ctx2, pats, opts)
2500 m = scmutil.match(ctx2, pats, opts)
2501 m = repo.narrowmatch(m)
2501 m = repo.narrowmatch(m)
2502 ui.pager(b'diff')
2502 ui.pager(b'diff')
2503 logcmdutil.diffordiffstat(
2503 logcmdutil.diffordiffstat(
2504 ui,
2504 ui,
2505 repo,
2505 repo,
2506 diffopts,
2506 diffopts,
2507 node1,
2507 node1,
2508 node2,
2508 node2,
2509 m,
2509 m,
2510 stat=stat,
2510 stat=stat,
2511 listsubrepos=opts.get(b'subrepos'),
2511 listsubrepos=opts.get(b'subrepos'),
2512 root=opts.get(b'root'),
2512 root=opts.get(b'root'),
2513 )
2513 )
2514
2514
2515
2515
2516 @command(
2516 @command(
2517 b'export',
2517 b'export',
2518 [
2518 [
2519 (
2519 (
2520 b'B',
2520 b'B',
2521 b'bookmark',
2521 b'bookmark',
2522 b'',
2522 b'',
2523 _(b'export changes only reachable by given bookmark'),
2523 _(b'export changes only reachable by given bookmark'),
2524 _(b'BOOKMARK'),
2524 _(b'BOOKMARK'),
2525 ),
2525 ),
2526 (
2526 (
2527 b'o',
2527 b'o',
2528 b'output',
2528 b'output',
2529 b'',
2529 b'',
2530 _(b'print output to file with formatted name'),
2530 _(b'print output to file with formatted name'),
2531 _(b'FORMAT'),
2531 _(b'FORMAT'),
2532 ),
2532 ),
2533 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2533 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2534 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2534 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2535 ]
2535 ]
2536 + diffopts
2536 + diffopts
2537 + formatteropts,
2537 + formatteropts,
2538 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2538 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2539 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2539 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2540 helpbasic=True,
2540 helpbasic=True,
2541 intents={INTENT_READONLY},
2541 intents={INTENT_READONLY},
2542 )
2542 )
2543 def export(ui, repo, *changesets, **opts):
2543 def export(ui, repo, *changesets, **opts):
2544 """dump the header and diffs for one or more changesets
2544 """dump the header and diffs for one or more changesets
2545
2545
2546 Print the changeset header and diffs for one or more revisions.
2546 Print the changeset header and diffs for one or more revisions.
2547 If no revision is given, the parent of the working directory is used.
2547 If no revision is given, the parent of the working directory is used.
2548
2548
2549 The information shown in the changeset header is: author, date,
2549 The information shown in the changeset header is: author, date,
2550 branch name (if non-default), changeset hash, parent(s) and commit
2550 branch name (if non-default), changeset hash, parent(s) and commit
2551 comment.
2551 comment.
2552
2552
2553 .. note::
2553 .. note::
2554
2554
2555 :hg:`export` may generate unexpected diff output for merge
2555 :hg:`export` may generate unexpected diff output for merge
2556 changesets, as it will compare the merge changeset against its
2556 changesets, as it will compare the merge changeset against its
2557 first parent only.
2557 first parent only.
2558
2558
2559 Output may be to a file, in which case the name of the file is
2559 Output may be to a file, in which case the name of the file is
2560 given using a template string. See :hg:`help templates`. In addition
2560 given using a template string. See :hg:`help templates`. In addition
2561 to the common template keywords, the following formatting rules are
2561 to the common template keywords, the following formatting rules are
2562 supported:
2562 supported:
2563
2563
2564 :``%%``: literal "%" character
2564 :``%%``: literal "%" character
2565 :``%H``: changeset hash (40 hexadecimal digits)
2565 :``%H``: changeset hash (40 hexadecimal digits)
2566 :``%N``: number of patches being generated
2566 :``%N``: number of patches being generated
2567 :``%R``: changeset revision number
2567 :``%R``: changeset revision number
2568 :``%b``: basename of the exporting repository
2568 :``%b``: basename of the exporting repository
2569 :``%h``: short-form changeset hash (12 hexadecimal digits)
2569 :``%h``: short-form changeset hash (12 hexadecimal digits)
2570 :``%m``: first line of the commit message (only alphanumeric characters)
2570 :``%m``: first line of the commit message (only alphanumeric characters)
2571 :``%n``: zero-padded sequence number, starting at 1
2571 :``%n``: zero-padded sequence number, starting at 1
2572 :``%r``: zero-padded changeset revision number
2572 :``%r``: zero-padded changeset revision number
2573 :``\\``: literal "\\" character
2573 :``\\``: literal "\\" character
2574
2574
2575 Without the -a/--text option, export will avoid generating diffs
2575 Without the -a/--text option, export will avoid generating diffs
2576 of files it detects as binary. With -a, export will generate a
2576 of files it detects as binary. With -a, export will generate a
2577 diff anyway, probably with undesirable results.
2577 diff anyway, probably with undesirable results.
2578
2578
2579 With -B/--bookmark changesets reachable by the given bookmark are
2579 With -B/--bookmark changesets reachable by the given bookmark are
2580 selected.
2580 selected.
2581
2581
2582 Use the -g/--git option to generate diffs in the git extended diff
2582 Use the -g/--git option to generate diffs in the git extended diff
2583 format. See :hg:`help diffs` for more information.
2583 format. See :hg:`help diffs` for more information.
2584
2584
2585 With the --switch-parent option, the diff will be against the
2585 With the --switch-parent option, the diff will be against the
2586 second parent. It can be useful to review a merge.
2586 second parent. It can be useful to review a merge.
2587
2587
2588 .. container:: verbose
2588 .. container:: verbose
2589
2589
2590 Template:
2590 Template:
2591
2591
2592 The following keywords are supported in addition to the common template
2592 The following keywords are supported in addition to the common template
2593 keywords and functions. See also :hg:`help templates`.
2593 keywords and functions. See also :hg:`help templates`.
2594
2594
2595 :diff: String. Diff content.
2595 :diff: String. Diff content.
2596 :parents: List of strings. Parent nodes of the changeset.
2596 :parents: List of strings. Parent nodes of the changeset.
2597
2597
2598 Examples:
2598 Examples:
2599
2599
2600 - use export and import to transplant a bugfix to the current
2600 - use export and import to transplant a bugfix to the current
2601 branch::
2601 branch::
2602
2602
2603 hg export -r 9353 | hg import -
2603 hg export -r 9353 | hg import -
2604
2604
2605 - export all the changesets between two revisions to a file with
2605 - export all the changesets between two revisions to a file with
2606 rename information::
2606 rename information::
2607
2607
2608 hg export --git -r 123:150 > changes.txt
2608 hg export --git -r 123:150 > changes.txt
2609
2609
2610 - split outgoing changes into a series of patches with
2610 - split outgoing changes into a series of patches with
2611 descriptive names::
2611 descriptive names::
2612
2612
2613 hg export -r "outgoing()" -o "%n-%m.patch"
2613 hg export -r "outgoing()" -o "%n-%m.patch"
2614
2614
2615 Returns 0 on success.
2615 Returns 0 on success.
2616 """
2616 """
2617 opts = pycompat.byteskwargs(opts)
2617 opts = pycompat.byteskwargs(opts)
2618 bookmark = opts.get(b'bookmark')
2618 bookmark = opts.get(b'bookmark')
2619 changesets += tuple(opts.get(b'rev', []))
2619 changesets += tuple(opts.get(b'rev', []))
2620
2620
2621 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2621 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2622
2622
2623 if bookmark:
2623 if bookmark:
2624 if bookmark not in repo._bookmarks:
2624 if bookmark not in repo._bookmarks:
2625 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2625 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2626
2626
2627 revs = scmutil.bookmarkrevs(repo, bookmark)
2627 revs = scmutil.bookmarkrevs(repo, bookmark)
2628 else:
2628 else:
2629 if not changesets:
2629 if not changesets:
2630 changesets = [b'.']
2630 changesets = [b'.']
2631
2631
2632 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2632 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2633 revs = scmutil.revrange(repo, changesets)
2633 revs = scmutil.revrange(repo, changesets)
2634
2634
2635 if not revs:
2635 if not revs:
2636 raise error.Abort(_(b"export requires at least one changeset"))
2636 raise error.Abort(_(b"export requires at least one changeset"))
2637 if len(revs) > 1:
2637 if len(revs) > 1:
2638 ui.note(_(b'exporting patches:\n'))
2638 ui.note(_(b'exporting patches:\n'))
2639 else:
2639 else:
2640 ui.note(_(b'exporting patch:\n'))
2640 ui.note(_(b'exporting patch:\n'))
2641
2641
2642 fntemplate = opts.get(b'output')
2642 fntemplate = opts.get(b'output')
2643 if cmdutil.isstdiofilename(fntemplate):
2643 if cmdutil.isstdiofilename(fntemplate):
2644 fntemplate = b''
2644 fntemplate = b''
2645
2645
2646 if fntemplate:
2646 if fntemplate:
2647 fm = formatter.nullformatter(ui, b'export', opts)
2647 fm = formatter.nullformatter(ui, b'export', opts)
2648 else:
2648 else:
2649 ui.pager(b'export')
2649 ui.pager(b'export')
2650 fm = ui.formatter(b'export', opts)
2650 fm = ui.formatter(b'export', opts)
2651 with fm:
2651 with fm:
2652 cmdutil.export(
2652 cmdutil.export(
2653 repo,
2653 repo,
2654 revs,
2654 revs,
2655 fm,
2655 fm,
2656 fntemplate=fntemplate,
2656 fntemplate=fntemplate,
2657 switch_parent=opts.get(b'switch_parent'),
2657 switch_parent=opts.get(b'switch_parent'),
2658 opts=patch.diffallopts(ui, opts),
2658 opts=patch.diffallopts(ui, opts),
2659 )
2659 )
2660
2660
2661
2661
2662 @command(
2662 @command(
2663 b'files',
2663 b'files',
2664 [
2664 [
2665 (
2665 (
2666 b'r',
2666 b'r',
2667 b'rev',
2667 b'rev',
2668 b'',
2668 b'',
2669 _(b'search the repository as it is in REV'),
2669 _(b'search the repository as it is in REV'),
2670 _(b'REV'),
2670 _(b'REV'),
2671 ),
2671 ),
2672 (
2672 (
2673 b'0',
2673 b'0',
2674 b'print0',
2674 b'print0',
2675 None,
2675 None,
2676 _(b'end filenames with NUL, for use with xargs'),
2676 _(b'end filenames with NUL, for use with xargs'),
2677 ),
2677 ),
2678 ]
2678 ]
2679 + walkopts
2679 + walkopts
2680 + formatteropts
2680 + formatteropts
2681 + subrepoopts,
2681 + subrepoopts,
2682 _(b'[OPTION]... [FILE]...'),
2682 _(b'[OPTION]... [FILE]...'),
2683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2684 intents={INTENT_READONLY},
2684 intents={INTENT_READONLY},
2685 )
2685 )
2686 def files(ui, repo, *pats, **opts):
2686 def files(ui, repo, *pats, **opts):
2687 """list tracked files
2687 """list tracked files
2688
2688
2689 Print files under Mercurial control in the working directory or
2689 Print files under Mercurial control in the working directory or
2690 specified revision for given files (excluding removed files).
2690 specified revision for given files (excluding removed files).
2691 Files can be specified as filenames or filesets.
2691 Files can be specified as filenames or filesets.
2692
2692
2693 If no files are given to match, this command prints the names
2693 If no files are given to match, this command prints the names
2694 of all files under Mercurial control.
2694 of all files under Mercurial control.
2695
2695
2696 .. container:: verbose
2696 .. container:: verbose
2697
2697
2698 Template:
2698 Template:
2699
2699
2700 The following keywords are supported in addition to the common template
2700 The following keywords are supported in addition to the common template
2701 keywords and functions. See also :hg:`help templates`.
2701 keywords and functions. See also :hg:`help templates`.
2702
2702
2703 :flags: String. Character denoting file's symlink and executable bits.
2703 :flags: String. Character denoting file's symlink and executable bits.
2704 :path: String. Repository-absolute path of the file.
2704 :path: String. Repository-absolute path of the file.
2705 :size: Integer. Size of the file in bytes.
2705 :size: Integer. Size of the file in bytes.
2706
2706
2707 Examples:
2707 Examples:
2708
2708
2709 - list all files under the current directory::
2709 - list all files under the current directory::
2710
2710
2711 hg files .
2711 hg files .
2712
2712
2713 - shows sizes and flags for current revision::
2713 - shows sizes and flags for current revision::
2714
2714
2715 hg files -vr .
2715 hg files -vr .
2716
2716
2717 - list all files named README::
2717 - list all files named README::
2718
2718
2719 hg files -I "**/README"
2719 hg files -I "**/README"
2720
2720
2721 - list all binary files::
2721 - list all binary files::
2722
2722
2723 hg files "set:binary()"
2723 hg files "set:binary()"
2724
2724
2725 - find files containing a regular expression::
2725 - find files containing a regular expression::
2726
2726
2727 hg files "set:grep('bob')"
2727 hg files "set:grep('bob')"
2728
2728
2729 - search tracked file contents with xargs and grep::
2729 - search tracked file contents with xargs and grep::
2730
2730
2731 hg files -0 | xargs -0 grep foo
2731 hg files -0 | xargs -0 grep foo
2732
2732
2733 See :hg:`help patterns` and :hg:`help filesets` for more information
2733 See :hg:`help patterns` and :hg:`help filesets` for more information
2734 on specifying file patterns.
2734 on specifying file patterns.
2735
2735
2736 Returns 0 if a match is found, 1 otherwise.
2736 Returns 0 if a match is found, 1 otherwise.
2737
2737
2738 """
2738 """
2739
2739
2740 opts = pycompat.byteskwargs(opts)
2740 opts = pycompat.byteskwargs(opts)
2741 rev = opts.get(b'rev')
2741 rev = opts.get(b'rev')
2742 if rev:
2742 if rev:
2743 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2743 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2744 ctx = scmutil.revsingle(repo, rev, None)
2744 ctx = scmutil.revsingle(repo, rev, None)
2745
2745
2746 end = b'\n'
2746 end = b'\n'
2747 if opts.get(b'print0'):
2747 if opts.get(b'print0'):
2748 end = b'\0'
2748 end = b'\0'
2749 fmt = b'%s' + end
2749 fmt = b'%s' + end
2750
2750
2751 m = scmutil.match(ctx, pats, opts)
2751 m = scmutil.match(ctx, pats, opts)
2752 ui.pager(b'files')
2752 ui.pager(b'files')
2753 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2753 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2754 with ui.formatter(b'files', opts) as fm:
2754 with ui.formatter(b'files', opts) as fm:
2755 return cmdutil.files(
2755 return cmdutil.files(
2756 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2756 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2757 )
2757 )
2758
2758
2759
2759
2760 @command(
2760 @command(
2761 b'forget',
2761 b'forget',
2762 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2762 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2763 + walkopts
2763 + walkopts
2764 + dryrunopts,
2764 + dryrunopts,
2765 _(b'[OPTION]... FILE...'),
2765 _(b'[OPTION]... FILE...'),
2766 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2766 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2767 helpbasic=True,
2767 helpbasic=True,
2768 inferrepo=True,
2768 inferrepo=True,
2769 )
2769 )
2770 def forget(ui, repo, *pats, **opts):
2770 def forget(ui, repo, *pats, **opts):
2771 """forget the specified files on the next commit
2771 """forget the specified files on the next commit
2772
2772
2773 Mark the specified files so they will no longer be tracked
2773 Mark the specified files so they will no longer be tracked
2774 after the next commit.
2774 after the next commit.
2775
2775
2776 This only removes files from the current branch, not from the
2776 This only removes files from the current branch, not from the
2777 entire project history, and it does not delete them from the
2777 entire project history, and it does not delete them from the
2778 working directory.
2778 working directory.
2779
2779
2780 To delete the file from the working directory, see :hg:`remove`.
2780 To delete the file from the working directory, see :hg:`remove`.
2781
2781
2782 To undo a forget before the next commit, see :hg:`add`.
2782 To undo a forget before the next commit, see :hg:`add`.
2783
2783
2784 .. container:: verbose
2784 .. container:: verbose
2785
2785
2786 Examples:
2786 Examples:
2787
2787
2788 - forget newly-added binary files::
2788 - forget newly-added binary files::
2789
2789
2790 hg forget "set:added() and binary()"
2790 hg forget "set:added() and binary()"
2791
2791
2792 - forget files that would be excluded by .hgignore::
2792 - forget files that would be excluded by .hgignore::
2793
2793
2794 hg forget "set:hgignore()"
2794 hg forget "set:hgignore()"
2795
2795
2796 Returns 0 on success.
2796 Returns 0 on success.
2797 """
2797 """
2798
2798
2799 opts = pycompat.byteskwargs(opts)
2799 opts = pycompat.byteskwargs(opts)
2800 if not pats:
2800 if not pats:
2801 raise error.Abort(_(b'no files specified'))
2801 raise error.Abort(_(b'no files specified'))
2802
2802
2803 m = scmutil.match(repo[None], pats, opts)
2803 m = scmutil.match(repo[None], pats, opts)
2804 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2804 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2805 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2805 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2806 rejected = cmdutil.forget(
2806 rejected = cmdutil.forget(
2807 ui,
2807 ui,
2808 repo,
2808 repo,
2809 m,
2809 m,
2810 prefix=b"",
2810 prefix=b"",
2811 uipathfn=uipathfn,
2811 uipathfn=uipathfn,
2812 explicitonly=False,
2812 explicitonly=False,
2813 dryrun=dryrun,
2813 dryrun=dryrun,
2814 interactive=interactive,
2814 interactive=interactive,
2815 )[0]
2815 )[0]
2816 return rejected and 1 or 0
2816 return rejected and 1 or 0
2817
2817
2818
2818
2819 @command(
2819 @command(
2820 b'graft',
2820 b'graft',
2821 [
2821 [
2822 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2822 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2823 (
2823 (
2824 b'',
2824 b'',
2825 b'base',
2825 b'base',
2826 b'',
2826 b'',
2827 _(b'base revision when doing the graft merge (ADVANCED)'),
2827 _(b'base revision when doing the graft merge (ADVANCED)'),
2828 _(b'REV'),
2828 _(b'REV'),
2829 ),
2829 ),
2830 (b'c', b'continue', False, _(b'resume interrupted graft')),
2830 (b'c', b'continue', False, _(b'resume interrupted graft')),
2831 (b'', b'stop', False, _(b'stop interrupted graft')),
2831 (b'', b'stop', False, _(b'stop interrupted graft')),
2832 (b'', b'abort', False, _(b'abort interrupted graft')),
2832 (b'', b'abort', False, _(b'abort interrupted graft')),
2833 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2833 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2834 (b'', b'log', None, _(b'append graft info to log message')),
2834 (b'', b'log', None, _(b'append graft info to log message')),
2835 (
2835 (
2836 b'',
2836 b'',
2837 b'no-commit',
2837 b'no-commit',
2838 None,
2838 None,
2839 _(b"don't commit, just apply the changes in working directory"),
2839 _(b"don't commit, just apply the changes in working directory"),
2840 ),
2840 ),
2841 (b'f', b'force', False, _(b'force graft')),
2841 (b'f', b'force', False, _(b'force graft')),
2842 (
2842 (
2843 b'D',
2843 b'D',
2844 b'currentdate',
2844 b'currentdate',
2845 False,
2845 False,
2846 _(b'record the current date as commit date'),
2846 _(b'record the current date as commit date'),
2847 ),
2847 ),
2848 (
2848 (
2849 b'U',
2849 b'U',
2850 b'currentuser',
2850 b'currentuser',
2851 False,
2851 False,
2852 _(b'record the current user as committer'),
2852 _(b'record the current user as committer'),
2853 ),
2853 ),
2854 ]
2854 ]
2855 + commitopts2
2855 + commitopts2
2856 + mergetoolopts
2856 + mergetoolopts
2857 + dryrunopts,
2857 + dryrunopts,
2858 _(b'[OPTION]... [-r REV]... REV...'),
2858 _(b'[OPTION]... [-r REV]... REV...'),
2859 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2859 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2860 )
2860 )
2861 def graft(ui, repo, *revs, **opts):
2861 def graft(ui, repo, *revs, **opts):
2862 '''copy changes from other branches onto the current branch
2862 '''copy changes from other branches onto the current branch
2863
2863
2864 This command uses Mercurial's merge logic to copy individual
2864 This command uses Mercurial's merge logic to copy individual
2865 changes from other branches without merging branches in the
2865 changes from other branches without merging branches in the
2866 history graph. This is sometimes known as 'backporting' or
2866 history graph. This is sometimes known as 'backporting' or
2867 'cherry-picking'. By default, graft will copy user, date, and
2867 'cherry-picking'. By default, graft will copy user, date, and
2868 description from the source changesets.
2868 description from the source changesets.
2869
2869
2870 Changesets that are ancestors of the current revision, that have
2870 Changesets that are ancestors of the current revision, that have
2871 already been grafted, or that are merges will be skipped.
2871 already been grafted, or that are merges will be skipped.
2872
2872
2873 If --log is specified, log messages will have a comment appended
2873 If --log is specified, log messages will have a comment appended
2874 of the form::
2874 of the form::
2875
2875
2876 (grafted from CHANGESETHASH)
2876 (grafted from CHANGESETHASH)
2877
2877
2878 If --force is specified, revisions will be grafted even if they
2878 If --force is specified, revisions will be grafted even if they
2879 are already ancestors of, or have been grafted to, the destination.
2879 are already ancestors of, or have been grafted to, the destination.
2880 This is useful when the revisions have since been backed out.
2880 This is useful when the revisions have since been backed out.
2881
2881
2882 If a graft merge results in conflicts, the graft process is
2882 If a graft merge results in conflicts, the graft process is
2883 interrupted so that the current merge can be manually resolved.
2883 interrupted so that the current merge can be manually resolved.
2884 Once all conflicts are addressed, the graft process can be
2884 Once all conflicts are addressed, the graft process can be
2885 continued with the -c/--continue option.
2885 continued with the -c/--continue option.
2886
2886
2887 The -c/--continue option reapplies all the earlier options.
2887 The -c/--continue option reapplies all the earlier options.
2888
2888
2889 .. container:: verbose
2889 .. container:: verbose
2890
2890
2891 The --base option exposes more of how graft internally uses merge with a
2891 The --base option exposes more of how graft internally uses merge with a
2892 custom base revision. --base can be used to specify another ancestor than
2892 custom base revision. --base can be used to specify another ancestor than
2893 the first and only parent.
2893 the first and only parent.
2894
2894
2895 The command::
2895 The command::
2896
2896
2897 hg graft -r 345 --base 234
2897 hg graft -r 345 --base 234
2898
2898
2899 is thus pretty much the same as::
2899 is thus pretty much the same as::
2900
2900
2901 hg diff -r 234 -r 345 | hg import
2901 hg diff -r 234 -r 345 | hg import
2902
2902
2903 but using merge to resolve conflicts and track moved files.
2903 but using merge to resolve conflicts and track moved files.
2904
2904
2905 The result of a merge can thus be backported as a single commit by
2905 The result of a merge can thus be backported as a single commit by
2906 specifying one of the merge parents as base, and thus effectively
2906 specifying one of the merge parents as base, and thus effectively
2907 grafting the changes from the other side.
2907 grafting the changes from the other side.
2908
2908
2909 It is also possible to collapse multiple changesets and clean up history
2909 It is also possible to collapse multiple changesets and clean up history
2910 by specifying another ancestor as base, much like rebase --collapse
2910 by specifying another ancestor as base, much like rebase --collapse
2911 --keep.
2911 --keep.
2912
2912
2913 The commit message can be tweaked after the fact using commit --amend .
2913 The commit message can be tweaked after the fact using commit --amend .
2914
2914
2915 For using non-ancestors as the base to backout changes, see the backout
2915 For using non-ancestors as the base to backout changes, see the backout
2916 command and the hidden --parent option.
2916 command and the hidden --parent option.
2917
2917
2918 .. container:: verbose
2918 .. container:: verbose
2919
2919
2920 Examples:
2920 Examples:
2921
2921
2922 - copy a single change to the stable branch and edit its description::
2922 - copy a single change to the stable branch and edit its description::
2923
2923
2924 hg update stable
2924 hg update stable
2925 hg graft --edit 9393
2925 hg graft --edit 9393
2926
2926
2927 - graft a range of changesets with one exception, updating dates::
2927 - graft a range of changesets with one exception, updating dates::
2928
2928
2929 hg graft -D "2085::2093 and not 2091"
2929 hg graft -D "2085::2093 and not 2091"
2930
2930
2931 - continue a graft after resolving conflicts::
2931 - continue a graft after resolving conflicts::
2932
2932
2933 hg graft -c
2933 hg graft -c
2934
2934
2935 - show the source of a grafted changeset::
2935 - show the source of a grafted changeset::
2936
2936
2937 hg log --debug -r .
2937 hg log --debug -r .
2938
2938
2939 - show revisions sorted by date::
2939 - show revisions sorted by date::
2940
2940
2941 hg log -r "sort(all(), date)"
2941 hg log -r "sort(all(), date)"
2942
2942
2943 - backport the result of a merge as a single commit::
2943 - backport the result of a merge as a single commit::
2944
2944
2945 hg graft -r 123 --base 123^
2945 hg graft -r 123 --base 123^
2946
2946
2947 - land a feature branch as one changeset::
2947 - land a feature branch as one changeset::
2948
2948
2949 hg up -cr default
2949 hg up -cr default
2950 hg graft -r featureX --base "ancestor('featureX', 'default')"
2950 hg graft -r featureX --base "ancestor('featureX', 'default')"
2951
2951
2952 See :hg:`help revisions` for more about specifying revisions.
2952 See :hg:`help revisions` for more about specifying revisions.
2953
2953
2954 Returns 0 on successful completion.
2954 Returns 0 on successful completion.
2955 '''
2955 '''
2956 with repo.wlock():
2956 with repo.wlock():
2957 return _dograft(ui, repo, *revs, **opts)
2957 return _dograft(ui, repo, *revs, **opts)
2958
2958
2959
2959
2960 def _dograft(ui, repo, *revs, **opts):
2960 def _dograft(ui, repo, *revs, **opts):
2961 opts = pycompat.byteskwargs(opts)
2961 opts = pycompat.byteskwargs(opts)
2962 if revs and opts.get(b'rev'):
2962 if revs and opts.get(b'rev'):
2963 ui.warn(
2963 ui.warn(
2964 _(
2964 _(
2965 b'warning: inconsistent use of --rev might give unexpected '
2965 b'warning: inconsistent use of --rev might give unexpected '
2966 b'revision ordering!\n'
2966 b'revision ordering!\n'
2967 )
2967 )
2968 )
2968 )
2969
2969
2970 revs = list(revs)
2970 revs = list(revs)
2971 revs.extend(opts.get(b'rev'))
2971 revs.extend(opts.get(b'rev'))
2972 basectx = None
2972 basectx = None
2973 if opts.get(b'base'):
2973 if opts.get(b'base'):
2974 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2974 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2975 # a dict of data to be stored in state file
2975 # a dict of data to be stored in state file
2976 statedata = {}
2976 statedata = {}
2977 # list of new nodes created by ongoing graft
2977 # list of new nodes created by ongoing graft
2978 statedata[b'newnodes'] = []
2978 statedata[b'newnodes'] = []
2979
2979
2980 cmdutil.resolvecommitoptions(ui, opts)
2980 cmdutil.resolvecommitoptions(ui, opts)
2981
2981
2982 editor = cmdutil.getcommiteditor(
2982 editor = cmdutil.getcommiteditor(
2983 editform=b'graft', **pycompat.strkwargs(opts)
2983 editform=b'graft', **pycompat.strkwargs(opts)
2984 )
2984 )
2985
2985
2986 cont = False
2986 cont = False
2987 if opts.get(b'no_commit'):
2987 if opts.get(b'no_commit'):
2988 if opts.get(b'edit'):
2988 if opts.get(b'edit'):
2989 raise error.Abort(
2989 raise error.Abort(
2990 _(b"cannot specify --no-commit and --edit together")
2990 _(b"cannot specify --no-commit and --edit together")
2991 )
2991 )
2992 if opts.get(b'currentuser'):
2992 if opts.get(b'currentuser'):
2993 raise error.Abort(
2993 raise error.Abort(
2994 _(b"cannot specify --no-commit and --currentuser together")
2994 _(b"cannot specify --no-commit and --currentuser together")
2995 )
2995 )
2996 if opts.get(b'currentdate'):
2996 if opts.get(b'currentdate'):
2997 raise error.Abort(
2997 raise error.Abort(
2998 _(b"cannot specify --no-commit and --currentdate together")
2998 _(b"cannot specify --no-commit and --currentdate together")
2999 )
2999 )
3000 if opts.get(b'log'):
3000 if opts.get(b'log'):
3001 raise error.Abort(
3001 raise error.Abort(
3002 _(b"cannot specify --no-commit and --log together")
3002 _(b"cannot specify --no-commit and --log together")
3003 )
3003 )
3004
3004
3005 graftstate = statemod.cmdstate(repo, b'graftstate')
3005 graftstate = statemod.cmdstate(repo, b'graftstate')
3006
3006
3007 if opts.get(b'stop'):
3007 if opts.get(b'stop'):
3008 if opts.get(b'continue'):
3008 if opts.get(b'continue'):
3009 raise error.Abort(
3009 raise error.Abort(
3010 _(b"cannot use '--continue' and '--stop' together")
3010 _(b"cannot use '--continue' and '--stop' together")
3011 )
3011 )
3012 if opts.get(b'abort'):
3012 if opts.get(b'abort'):
3013 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3013 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3014
3014
3015 if any(
3015 if any(
3016 (
3016 (
3017 opts.get(b'edit'),
3017 opts.get(b'edit'),
3018 opts.get(b'log'),
3018 opts.get(b'log'),
3019 opts.get(b'user'),
3019 opts.get(b'user'),
3020 opts.get(b'date'),
3020 opts.get(b'date'),
3021 opts.get(b'currentdate'),
3021 opts.get(b'currentdate'),
3022 opts.get(b'currentuser'),
3022 opts.get(b'currentuser'),
3023 opts.get(b'rev'),
3023 opts.get(b'rev'),
3024 )
3024 )
3025 ):
3025 ):
3026 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3026 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3027 return _stopgraft(ui, repo, graftstate)
3027 return _stopgraft(ui, repo, graftstate)
3028 elif opts.get(b'abort'):
3028 elif opts.get(b'abort'):
3029 if opts.get(b'continue'):
3029 if opts.get(b'continue'):
3030 raise error.Abort(
3030 raise error.Abort(
3031 _(b"cannot use '--continue' and '--abort' together")
3031 _(b"cannot use '--continue' and '--abort' together")
3032 )
3032 )
3033 if any(
3033 if any(
3034 (
3034 (
3035 opts.get(b'edit'),
3035 opts.get(b'edit'),
3036 opts.get(b'log'),
3036 opts.get(b'log'),
3037 opts.get(b'user'),
3037 opts.get(b'user'),
3038 opts.get(b'date'),
3038 opts.get(b'date'),
3039 opts.get(b'currentdate'),
3039 opts.get(b'currentdate'),
3040 opts.get(b'currentuser'),
3040 opts.get(b'currentuser'),
3041 opts.get(b'rev'),
3041 opts.get(b'rev'),
3042 )
3042 )
3043 ):
3043 ):
3044 raise error.Abort(
3044 raise error.Abort(
3045 _(b"cannot specify any other flag with '--abort'")
3045 _(b"cannot specify any other flag with '--abort'")
3046 )
3046 )
3047
3047
3048 return cmdutil.abortgraft(ui, repo, graftstate)
3048 return cmdutil.abortgraft(ui, repo, graftstate)
3049 elif opts.get(b'continue'):
3049 elif opts.get(b'continue'):
3050 cont = True
3050 cont = True
3051 if revs:
3051 if revs:
3052 raise error.Abort(_(b"can't specify --continue and revisions"))
3052 raise error.Abort(_(b"can't specify --continue and revisions"))
3053 # read in unfinished revisions
3053 # read in unfinished revisions
3054 if graftstate.exists():
3054 if graftstate.exists():
3055 statedata = cmdutil.readgraftstate(repo, graftstate)
3055 statedata = cmdutil.readgraftstate(repo, graftstate)
3056 if statedata.get(b'date'):
3056 if statedata.get(b'date'):
3057 opts[b'date'] = statedata[b'date']
3057 opts[b'date'] = statedata[b'date']
3058 if statedata.get(b'user'):
3058 if statedata.get(b'user'):
3059 opts[b'user'] = statedata[b'user']
3059 opts[b'user'] = statedata[b'user']
3060 if statedata.get(b'log'):
3060 if statedata.get(b'log'):
3061 opts[b'log'] = True
3061 opts[b'log'] = True
3062 if statedata.get(b'no_commit'):
3062 if statedata.get(b'no_commit'):
3063 opts[b'no_commit'] = statedata.get(b'no_commit')
3063 opts[b'no_commit'] = statedata.get(b'no_commit')
3064 nodes = statedata[b'nodes']
3064 nodes = statedata[b'nodes']
3065 revs = [repo[node].rev() for node in nodes]
3065 revs = [repo[node].rev() for node in nodes]
3066 else:
3066 else:
3067 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3067 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3068 else:
3068 else:
3069 if not revs:
3069 if not revs:
3070 raise error.Abort(_(b'no revisions specified'))
3070 raise error.Abort(_(b'no revisions specified'))
3071 cmdutil.checkunfinished(repo)
3071 cmdutil.checkunfinished(repo)
3072 cmdutil.bailifchanged(repo)
3072 cmdutil.bailifchanged(repo)
3073 revs = scmutil.revrange(repo, revs)
3073 revs = scmutil.revrange(repo, revs)
3074
3074
3075 skipped = set()
3075 skipped = set()
3076 if basectx is None:
3076 if basectx is None:
3077 # check for merges
3077 # check for merges
3078 for rev in repo.revs(b'%ld and merge()', revs):
3078 for rev in repo.revs(b'%ld and merge()', revs):
3079 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3079 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3080 skipped.add(rev)
3080 skipped.add(rev)
3081 revs = [r for r in revs if r not in skipped]
3081 revs = [r for r in revs if r not in skipped]
3082 if not revs:
3082 if not revs:
3083 return -1
3083 return -1
3084 if basectx is not None and len(revs) != 1:
3084 if basectx is not None and len(revs) != 1:
3085 raise error.Abort(_(b'only one revision allowed with --base '))
3085 raise error.Abort(_(b'only one revision allowed with --base '))
3086
3086
3087 # Don't check in the --continue case, in effect retaining --force across
3087 # Don't check in the --continue case, in effect retaining --force across
3088 # --continues. That's because without --force, any revisions we decided to
3088 # --continues. That's because without --force, any revisions we decided to
3089 # skip would have been filtered out here, so they wouldn't have made their
3089 # skip would have been filtered out here, so they wouldn't have made their
3090 # way to the graftstate. With --force, any revisions we would have otherwise
3090 # way to the graftstate. With --force, any revisions we would have otherwise
3091 # skipped would not have been filtered out, and if they hadn't been applied
3091 # skipped would not have been filtered out, and if they hadn't been applied
3092 # already, they'd have been in the graftstate.
3092 # already, they'd have been in the graftstate.
3093 if not (cont or opts.get(b'force')) and basectx is None:
3093 if not (cont or opts.get(b'force')) and basectx is None:
3094 # check for ancestors of dest branch
3094 # check for ancestors of dest branch
3095 ancestors = repo.revs(b'%ld & (::.)', revs)
3095 ancestors = repo.revs(b'%ld & (::.)', revs)
3096 for rev in ancestors:
3096 for rev in ancestors:
3097 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3097 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3098
3098
3099 revs = [r for r in revs if r not in ancestors]
3099 revs = [r for r in revs if r not in ancestors]
3100
3100
3101 if not revs:
3101 if not revs:
3102 return -1
3102 return -1
3103
3103
3104 # analyze revs for earlier grafts
3104 # analyze revs for earlier grafts
3105 ids = {}
3105 ids = {}
3106 for ctx in repo.set(b"%ld", revs):
3106 for ctx in repo.set(b"%ld", revs):
3107 ids[ctx.hex()] = ctx.rev()
3107 ids[ctx.hex()] = ctx.rev()
3108 n = ctx.extra().get(b'source')
3108 n = ctx.extra().get(b'source')
3109 if n:
3109 if n:
3110 ids[n] = ctx.rev()
3110 ids[n] = ctx.rev()
3111
3111
3112 # check ancestors for earlier grafts
3112 # check ancestors for earlier grafts
3113 ui.debug(b'scanning for duplicate grafts\n')
3113 ui.debug(b'scanning for duplicate grafts\n')
3114
3114
3115 # The only changesets we can be sure doesn't contain grafts of any
3115 # The only changesets we can be sure doesn't contain grafts of any
3116 # revs, are the ones that are common ancestors of *all* revs:
3116 # revs, are the ones that are common ancestors of *all* revs:
3117 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3117 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3118 ctx = repo[rev]
3118 ctx = repo[rev]
3119 n = ctx.extra().get(b'source')
3119 n = ctx.extra().get(b'source')
3120 if n in ids:
3120 if n in ids:
3121 try:
3121 try:
3122 r = repo[n].rev()
3122 r = repo[n].rev()
3123 except error.RepoLookupError:
3123 except error.RepoLookupError:
3124 r = None
3124 r = None
3125 if r in revs:
3125 if r in revs:
3126 ui.warn(
3126 ui.warn(
3127 _(
3127 _(
3128 b'skipping revision %d:%s '
3128 b'skipping revision %d:%s '
3129 b'(already grafted to %d:%s)\n'
3129 b'(already grafted to %d:%s)\n'
3130 )
3130 )
3131 % (r, repo[r], rev, ctx)
3131 % (r, repo[r], rev, ctx)
3132 )
3132 )
3133 revs.remove(r)
3133 revs.remove(r)
3134 elif ids[n] in revs:
3134 elif ids[n] in revs:
3135 if r is None:
3135 if r is None:
3136 ui.warn(
3136 ui.warn(
3137 _(
3137 _(
3138 b'skipping already grafted revision %d:%s '
3138 b'skipping already grafted revision %d:%s '
3139 b'(%d:%s also has unknown origin %s)\n'
3139 b'(%d:%s also has unknown origin %s)\n'
3140 )
3140 )
3141 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3141 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3142 )
3142 )
3143 else:
3143 else:
3144 ui.warn(
3144 ui.warn(
3145 _(
3145 _(
3146 b'skipping already grafted revision %d:%s '
3146 b'skipping already grafted revision %d:%s '
3147 b'(%d:%s also has origin %d:%s)\n'
3147 b'(%d:%s also has origin %d:%s)\n'
3148 )
3148 )
3149 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3149 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3150 )
3150 )
3151 revs.remove(ids[n])
3151 revs.remove(ids[n])
3152 elif ctx.hex() in ids:
3152 elif ctx.hex() in ids:
3153 r = ids[ctx.hex()]
3153 r = ids[ctx.hex()]
3154 if r in revs:
3154 if r in revs:
3155 ui.warn(
3155 ui.warn(
3156 _(
3156 _(
3157 b'skipping already grafted revision %d:%s '
3157 b'skipping already grafted revision %d:%s '
3158 b'(was grafted from %d:%s)\n'
3158 b'(was grafted from %d:%s)\n'
3159 )
3159 )
3160 % (r, repo[r], rev, ctx)
3160 % (r, repo[r], rev, ctx)
3161 )
3161 )
3162 revs.remove(r)
3162 revs.remove(r)
3163 if not revs:
3163 if not revs:
3164 return -1
3164 return -1
3165
3165
3166 if opts.get(b'no_commit'):
3166 if opts.get(b'no_commit'):
3167 statedata[b'no_commit'] = True
3167 statedata[b'no_commit'] = True
3168 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3168 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3169 desc = b'%d:%s "%s"' % (
3169 desc = b'%d:%s "%s"' % (
3170 ctx.rev(),
3170 ctx.rev(),
3171 ctx,
3171 ctx,
3172 ctx.description().split(b'\n', 1)[0],
3172 ctx.description().split(b'\n', 1)[0],
3173 )
3173 )
3174 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3174 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3175 if names:
3175 if names:
3176 desc += b' (%s)' % b' '.join(names)
3176 desc += b' (%s)' % b' '.join(names)
3177 ui.status(_(b'grafting %s\n') % desc)
3177 ui.status(_(b'grafting %s\n') % desc)
3178 if opts.get(b'dry_run'):
3178 if opts.get(b'dry_run'):
3179 continue
3179 continue
3180
3180
3181 source = ctx.extra().get(b'source')
3181 source = ctx.extra().get(b'source')
3182 extra = {}
3182 extra = {}
3183 if source:
3183 if source:
3184 extra[b'source'] = source
3184 extra[b'source'] = source
3185 extra[b'intermediate-source'] = ctx.hex()
3185 extra[b'intermediate-source'] = ctx.hex()
3186 else:
3186 else:
3187 extra[b'source'] = ctx.hex()
3187 extra[b'source'] = ctx.hex()
3188 user = ctx.user()
3188 user = ctx.user()
3189 if opts.get(b'user'):
3189 if opts.get(b'user'):
3190 user = opts[b'user']
3190 user = opts[b'user']
3191 statedata[b'user'] = user
3191 statedata[b'user'] = user
3192 date = ctx.date()
3192 date = ctx.date()
3193 if opts.get(b'date'):
3193 if opts.get(b'date'):
3194 date = opts[b'date']
3194 date = opts[b'date']
3195 statedata[b'date'] = date
3195 statedata[b'date'] = date
3196 message = ctx.description()
3196 message = ctx.description()
3197 if opts.get(b'log'):
3197 if opts.get(b'log'):
3198 message += b'\n(grafted from %s)' % ctx.hex()
3198 message += b'\n(grafted from %s)' % ctx.hex()
3199 statedata[b'log'] = True
3199 statedata[b'log'] = True
3200
3200
3201 # we don't merge the first commit when continuing
3201 # we don't merge the first commit when continuing
3202 if not cont:
3202 if not cont:
3203 # perform the graft merge with p1(rev) as 'ancestor'
3203 # perform the graft merge with p1(rev) as 'ancestor'
3204 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3204 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3205 base = ctx.p1() if basectx is None else basectx
3205 base = ctx.p1() if basectx is None else basectx
3206 with ui.configoverride(overrides, b'graft'):
3206 with ui.configoverride(overrides, b'graft'):
3207 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3207 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3208 # report any conflicts
3208 # report any conflicts
3209 if stats.unresolvedcount > 0:
3209 if stats.unresolvedcount > 0:
3210 # write out state for --continue
3210 # write out state for --continue
3211 nodes = [repo[rev].hex() for rev in revs[pos:]]
3211 nodes = [repo[rev].hex() for rev in revs[pos:]]
3212 statedata[b'nodes'] = nodes
3212 statedata[b'nodes'] = nodes
3213 stateversion = 1
3213 stateversion = 1
3214 graftstate.save(stateversion, statedata)
3214 graftstate.save(stateversion, statedata)
3215 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3215 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3216 raise error.Abort(
3216 raise error.Abort(
3217 _(b"unresolved conflicts, can't continue"), hint=hint
3217 _(b"unresolved conflicts, can't continue"), hint=hint
3218 )
3218 )
3219 else:
3219 else:
3220 cont = False
3220 cont = False
3221
3221
3222 # commit if --no-commit is false
3222 # commit if --no-commit is false
3223 if not opts.get(b'no_commit'):
3223 if not opts.get(b'no_commit'):
3224 node = repo.commit(
3224 node = repo.commit(
3225 text=message, user=user, date=date, extra=extra, editor=editor
3225 text=message, user=user, date=date, extra=extra, editor=editor
3226 )
3226 )
3227 if node is None:
3227 if node is None:
3228 ui.warn(
3228 ui.warn(
3229 _(b'note: graft of %d:%s created no changes to commit\n')
3229 _(b'note: graft of %d:%s created no changes to commit\n')
3230 % (ctx.rev(), ctx)
3230 % (ctx.rev(), ctx)
3231 )
3231 )
3232 # checking that newnodes exist because old state files won't have it
3232 # checking that newnodes exist because old state files won't have it
3233 elif statedata.get(b'newnodes') is not None:
3233 elif statedata.get(b'newnodes') is not None:
3234 statedata[b'newnodes'].append(node)
3234 statedata[b'newnodes'].append(node)
3235
3235
3236 # remove state when we complete successfully
3236 # remove state when we complete successfully
3237 if not opts.get(b'dry_run'):
3237 if not opts.get(b'dry_run'):
3238 graftstate.delete()
3238 graftstate.delete()
3239
3239
3240 return 0
3240 return 0
3241
3241
3242
3242
3243 def _stopgraft(ui, repo, graftstate):
3243 def _stopgraft(ui, repo, graftstate):
3244 """stop the interrupted graft"""
3244 """stop the interrupted graft"""
3245 if not graftstate.exists():
3245 if not graftstate.exists():
3246 raise error.Abort(_(b"no interrupted graft found"))
3246 raise error.Abort(_(b"no interrupted graft found"))
3247 pctx = repo[b'.']
3247 pctx = repo[b'.']
3248 hg.updaterepo(repo, pctx.node(), overwrite=True)
3248 hg.updaterepo(repo, pctx.node(), overwrite=True)
3249 graftstate.delete()
3249 graftstate.delete()
3250 ui.status(_(b"stopped the interrupted graft\n"))
3250 ui.status(_(b"stopped the interrupted graft\n"))
3251 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3251 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3252 return 0
3252 return 0
3253
3253
3254
3254
3255 statemod.addunfinished(
3255 statemod.addunfinished(
3256 b'graft',
3256 b'graft',
3257 fname=b'graftstate',
3257 fname=b'graftstate',
3258 clearable=True,
3258 clearable=True,
3259 stopflag=True,
3259 stopflag=True,
3260 continueflag=True,
3260 continueflag=True,
3261 abortfunc=cmdutil.hgabortgraft,
3261 abortfunc=cmdutil.hgabortgraft,
3262 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3262 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3263 )
3263 )
3264
3264
3265
3265
3266 @command(
3266 @command(
3267 b'grep',
3267 b'grep',
3268 [
3268 [
3269 (b'0', b'print0', None, _(b'end fields with NUL')),
3269 (b'0', b'print0', None, _(b'end fields with NUL')),
3270 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3270 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3271 (
3271 (
3272 b'',
3272 b'',
3273 b'diff',
3273 b'diff',
3274 None,
3274 None,
3275 _(
3275 _(
3276 b'search revision differences for when the pattern was added '
3276 b'search revision differences for when the pattern was added '
3277 b'or removed'
3277 b'or removed'
3278 ),
3278 ),
3279 ),
3279 ),
3280 (b'a', b'text', None, _(b'treat all files as text')),
3280 (b'a', b'text', None, _(b'treat all files as text')),
3281 (
3281 (
3282 b'f',
3282 b'f',
3283 b'follow',
3283 b'follow',
3284 None,
3284 None,
3285 _(
3285 _(
3286 b'follow changeset history,'
3286 b'follow changeset history,'
3287 b' or file history across copies and renames'
3287 b' or file history across copies and renames'
3288 ),
3288 ),
3289 ),
3289 ),
3290 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3290 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3291 (
3291 (
3292 b'l',
3292 b'l',
3293 b'files-with-matches',
3293 b'files-with-matches',
3294 None,
3294 None,
3295 _(b'print only filenames and revisions that match'),
3295 _(b'print only filenames and revisions that match'),
3296 ),
3296 ),
3297 (b'n', b'line-number', None, _(b'print matching line numbers')),
3297 (b'n', b'line-number', None, _(b'print matching line numbers')),
3298 (
3298 (
3299 b'r',
3299 b'r',
3300 b'rev',
3300 b'rev',
3301 [],
3301 [],
3302 _(b'search files changed within revision range'),
3302 _(b'search files changed within revision range'),
3303 _(b'REV'),
3303 _(b'REV'),
3304 ),
3304 ),
3305 (
3305 (
3306 b'',
3306 b'',
3307 b'all-files',
3307 b'all-files',
3308 None,
3308 None,
3309 _(
3309 _(
3310 b'include all files in the changeset while grepping (DEPRECATED)'
3310 b'include all files in the changeset while grepping (DEPRECATED)'
3311 ),
3311 ),
3312 ),
3312 ),
3313 (b'u', b'user', None, _(b'list the author (long with -v)')),
3313 (b'u', b'user', None, _(b'list the author (long with -v)')),
3314 (b'd', b'date', None, _(b'list the date (short with -q)')),
3314 (b'd', b'date', None, _(b'list the date (short with -q)')),
3315 ]
3315 ]
3316 + formatteropts
3316 + formatteropts
3317 + walkopts,
3317 + walkopts,
3318 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3318 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3319 helpcategory=command.CATEGORY_FILE_CONTENTS,
3319 helpcategory=command.CATEGORY_FILE_CONTENTS,
3320 inferrepo=True,
3320 inferrepo=True,
3321 intents={INTENT_READONLY},
3321 intents={INTENT_READONLY},
3322 )
3322 )
3323 def grep(ui, repo, pattern, *pats, **opts):
3323 def grep(ui, repo, pattern, *pats, **opts):
3324 """search for a pattern in specified files
3324 """search for a pattern in specified files
3325
3325
3326 Search the working directory or revision history for a regular
3326 Search the working directory or revision history for a regular
3327 expression in the specified files for the entire repository.
3327 expression in the specified files for the entire repository.
3328
3328
3329 By default, grep searches the repository files in the working
3329 By default, grep searches the repository files in the working
3330 directory and prints the files where it finds a match. To specify
3330 directory and prints the files where it finds a match. To specify
3331 historical revisions instead of the working directory, use the
3331 historical revisions instead of the working directory, use the
3332 --rev flag.
3332 --rev flag.
3333
3333
3334 To search instead historical revision differences that contains a
3334 To search instead historical revision differences that contains a
3335 change in match status ("-" for a match that becomes a non-match,
3335 change in match status ("-" for a match that becomes a non-match,
3336 or "+" for a non-match that becomes a match), use the --diff flag.
3336 or "+" for a non-match that becomes a match), use the --diff flag.
3337
3337
3338 PATTERN can be any Python (roughly Perl-compatible) regular
3338 PATTERN can be any Python (roughly Perl-compatible) regular
3339 expression.
3339 expression.
3340
3340
3341 If no FILEs are specified and the --rev flag isn't supplied, all
3341 If no FILEs are specified and the --rev flag isn't supplied, all
3342 files in the working directory are searched. When using the --rev
3342 files in the working directory are searched. When using the --rev
3343 flag and specifying FILEs, use the --follow argument to also
3343 flag and specifying FILEs, use the --follow argument to also
3344 follow the specified FILEs across renames and copies.
3344 follow the specified FILEs across renames and copies.
3345
3345
3346 .. container:: verbose
3346 .. container:: verbose
3347
3347
3348 Template:
3348 Template:
3349
3349
3350 The following keywords are supported in addition to the common template
3350 The following keywords are supported in addition to the common template
3351 keywords and functions. See also :hg:`help templates`.
3351 keywords and functions. See also :hg:`help templates`.
3352
3352
3353 :change: String. Character denoting insertion ``+`` or removal ``-``.
3353 :change: String. Character denoting insertion ``+`` or removal ``-``.
3354 Available if ``--diff`` is specified.
3354 Available if ``--diff`` is specified.
3355 :lineno: Integer. Line number of the match.
3355 :lineno: Integer. Line number of the match.
3356 :path: String. Repository-absolute path of the file.
3356 :path: String. Repository-absolute path of the file.
3357 :texts: List of text chunks.
3357 :texts: List of text chunks.
3358
3358
3359 And each entry of ``{texts}`` provides the following sub-keywords.
3359 And each entry of ``{texts}`` provides the following sub-keywords.
3360
3360
3361 :matched: Boolean. True if the chunk matches the specified pattern.
3361 :matched: Boolean. True if the chunk matches the specified pattern.
3362 :text: String. Chunk content.
3362 :text: String. Chunk content.
3363
3363
3364 See :hg:`help templates.operators` for the list expansion syntax.
3364 See :hg:`help templates.operators` for the list expansion syntax.
3365
3365
3366 Returns 0 if a match is found, 1 otherwise.
3366 Returns 0 if a match is found, 1 otherwise.
3367
3367
3368 """
3368 """
3369 opts = pycompat.byteskwargs(opts)
3369 opts = pycompat.byteskwargs(opts)
3370 diff = opts.get(b'all') or opts.get(b'diff')
3370 diff = opts.get(b'all') or opts.get(b'diff')
3371 if diff and opts.get(b'all_files'):
3371 if diff and opts.get(b'all_files'):
3372 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3372 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3373 if opts.get(b'all_files') is None and not diff:
3373 if opts.get(b'all_files') is None and not diff:
3374 opts[b'all_files'] = True
3374 opts[b'all_files'] = True
3375 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3375 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3376 all_files = opts.get(b'all_files')
3376 all_files = opts.get(b'all_files')
3377 if plaingrep:
3377 if plaingrep:
3378 opts[b'rev'] = [b'wdir()']
3378 opts[b'rev'] = [b'wdir()']
3379
3379
3380 reflags = re.M
3380 reflags = re.M
3381 if opts.get(b'ignore_case'):
3381 if opts.get(b'ignore_case'):
3382 reflags |= re.I
3382 reflags |= re.I
3383 try:
3383 try:
3384 regexp = util.re.compile(pattern, reflags)
3384 regexp = util.re.compile(pattern, reflags)
3385 except re.error as inst:
3385 except re.error as inst:
3386 ui.warn(
3386 ui.warn(
3387 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3387 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3388 )
3388 )
3389 return 1
3389 return 1
3390 sep, eol = b':', b'\n'
3390 sep, eol = b':', b'\n'
3391 if opts.get(b'print0'):
3391 if opts.get(b'print0'):
3392 sep = eol = b'\0'
3392 sep = eol = b'\0'
3393
3393
3394 getfile = util.lrucachefunc(repo.file)
3394 getfile = util.lrucachefunc(repo.file)
3395
3395
3396 def matchlines(body):
3396 def matchlines(body):
3397 begin = 0
3397 begin = 0
3398 linenum = 0
3398 linenum = 0
3399 while begin < len(body):
3399 while begin < len(body):
3400 match = regexp.search(body, begin)
3400 match = regexp.search(body, begin)
3401 if not match:
3401 if not match:
3402 break
3402 break
3403 mstart, mend = match.span()
3403 mstart, mend = match.span()
3404 linenum += body.count(b'\n', begin, mstart) + 1
3404 linenum += body.count(b'\n', begin, mstart) + 1
3405 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3405 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3406 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3406 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3407 lend = begin - 1
3407 lend = begin - 1
3408 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3408 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3409
3409
3410 class linestate(object):
3410 class linestate(object):
3411 def __init__(self, line, linenum, colstart, colend):
3411 def __init__(self, line, linenum, colstart, colend):
3412 self.line = line
3412 self.line = line
3413 self.linenum = linenum
3413 self.linenum = linenum
3414 self.colstart = colstart
3414 self.colstart = colstart
3415 self.colend = colend
3415 self.colend = colend
3416
3416
3417 def __hash__(self):
3417 def __hash__(self):
3418 return hash((self.linenum, self.line))
3418 return hash((self.linenum, self.line))
3419
3419
3420 def __eq__(self, other):
3420 def __eq__(self, other):
3421 return self.line == other.line
3421 return self.line == other.line
3422
3422
3423 def findpos(self):
3423 def findpos(self):
3424 """Iterate all (start, end) indices of matches"""
3424 """Iterate all (start, end) indices of matches"""
3425 yield self.colstart, self.colend
3425 yield self.colstart, self.colend
3426 p = self.colend
3426 p = self.colend
3427 while p < len(self.line):
3427 while p < len(self.line):
3428 m = regexp.search(self.line, p)
3428 m = regexp.search(self.line, p)
3429 if not m:
3429 if not m:
3430 break
3430 break
3431 yield m.span()
3431 yield m.span()
3432 p = m.end()
3432 p = m.end()
3433
3433
3434 matches = {}
3434 matches = {}
3435 copies = {}
3435 copies = {}
3436
3436
3437 def grepbody(fn, rev, body):
3437 def grepbody(fn, rev, body):
3438 matches[rev].setdefault(fn, [])
3438 matches[rev].setdefault(fn, [])
3439 m = matches[rev][fn]
3439 m = matches[rev][fn]
3440 if body is None:
3440 if body is None:
3441 return
3441 return
3442
3442
3443 for lnum, cstart, cend, line in matchlines(body):
3443 for lnum, cstart, cend, line in matchlines(body):
3444 s = linestate(line, lnum, cstart, cend)
3444 s = linestate(line, lnum, cstart, cend)
3445 m.append(s)
3445 m.append(s)
3446
3446
3447 def difflinestates(a, b):
3447 def difflinestates(a, b):
3448 sm = difflib.SequenceMatcher(None, a, b)
3448 sm = difflib.SequenceMatcher(None, a, b)
3449 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3449 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3450 if tag == 'insert':
3450 if tag == 'insert':
3451 for i in pycompat.xrange(blo, bhi):
3451 for i in pycompat.xrange(blo, bhi):
3452 yield (b'+', b[i])
3452 yield (b'+', b[i])
3453 elif tag == 'delete':
3453 elif tag == 'delete':
3454 for i in pycompat.xrange(alo, ahi):
3454 for i in pycompat.xrange(alo, ahi):
3455 yield (b'-', a[i])
3455 yield (b'-', a[i])
3456 elif tag == 'replace':
3456 elif tag == 'replace':
3457 for i in pycompat.xrange(alo, ahi):
3457 for i in pycompat.xrange(alo, ahi):
3458 yield (b'-', a[i])
3458 yield (b'-', a[i])
3459 for i in pycompat.xrange(blo, bhi):
3459 for i in pycompat.xrange(blo, bhi):
3460 yield (b'+', b[i])
3460 yield (b'+', b[i])
3461
3461
3462 uipathfn = scmutil.getuipathfn(repo)
3462 uipathfn = scmutil.getuipathfn(repo)
3463
3463
3464 def display(fm, fn, ctx, pstates, states):
3464 def display(fm, fn, ctx, pstates, states):
3465 rev = scmutil.intrev(ctx)
3465 rev = scmutil.intrev(ctx)
3466 if fm.isplain():
3466 if fm.isplain():
3467 formatuser = ui.shortuser
3467 formatuser = ui.shortuser
3468 else:
3468 else:
3469 formatuser = pycompat.bytestr
3469 formatuser = pycompat.bytestr
3470 if ui.quiet:
3470 if ui.quiet:
3471 datefmt = b'%Y-%m-%d'
3471 datefmt = b'%Y-%m-%d'
3472 else:
3472 else:
3473 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3473 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3474 found = False
3474 found = False
3475
3475
3476 @util.cachefunc
3476 @util.cachefunc
3477 def binary():
3477 def binary():
3478 flog = getfile(fn)
3478 flog = getfile(fn)
3479 try:
3479 try:
3480 return stringutil.binary(flog.read(ctx.filenode(fn)))
3480 return stringutil.binary(flog.read(ctx.filenode(fn)))
3481 except error.WdirUnsupported:
3481 except error.WdirUnsupported:
3482 return ctx[fn].isbinary()
3482 return ctx[fn].isbinary()
3483
3483
3484 fieldnamemap = {b'linenumber': b'lineno'}
3484 fieldnamemap = {b'linenumber': b'lineno'}
3485 if diff:
3485 if diff:
3486 iter = difflinestates(pstates, states)
3486 iter = difflinestates(pstates, states)
3487 else:
3487 else:
3488 iter = [(b'', l) for l in states]
3488 iter = [(b'', l) for l in states]
3489 for change, l in iter:
3489 for change, l in iter:
3490 fm.startitem()
3490 fm.startitem()
3491 fm.context(ctx=ctx)
3491 fm.context(ctx=ctx)
3492 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3492 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3493 fm.plain(uipathfn(fn), label=b'grep.filename')
3493 fm.plain(uipathfn(fn), label=b'grep.filename')
3494
3494
3495 cols = [
3495 cols = [
3496 (b'rev', b'%d', rev, not plaingrep, b''),
3496 (b'rev', b'%d', rev, not plaingrep, b''),
3497 (
3497 (
3498 b'linenumber',
3498 b'linenumber',
3499 b'%d',
3499 b'%d',
3500 l.linenum,
3500 l.linenum,
3501 opts.get(b'line_number'),
3501 opts.get(b'line_number'),
3502 b'',
3502 b'',
3503 ),
3503 ),
3504 ]
3504 ]
3505 if diff:
3505 if diff:
3506 cols.append(
3506 cols.append(
3507 (
3507 (
3508 b'change',
3508 b'change',
3509 b'%s',
3509 b'%s',
3510 change,
3510 change,
3511 True,
3511 True,
3512 b'grep.inserted '
3512 b'grep.inserted '
3513 if change == b'+'
3513 if change == b'+'
3514 else b'grep.deleted ',
3514 else b'grep.deleted ',
3515 )
3515 )
3516 )
3516 )
3517 cols.extend(
3517 cols.extend(
3518 [
3518 [
3519 (
3519 (
3520 b'user',
3520 b'user',
3521 b'%s',
3521 b'%s',
3522 formatuser(ctx.user()),
3522 formatuser(ctx.user()),
3523 opts.get(b'user'),
3523 opts.get(b'user'),
3524 b'',
3524 b'',
3525 ),
3525 ),
3526 (
3526 (
3527 b'date',
3527 b'date',
3528 b'%s',
3528 b'%s',
3529 fm.formatdate(ctx.date(), datefmt),
3529 fm.formatdate(ctx.date(), datefmt),
3530 opts.get(b'date'),
3530 opts.get(b'date'),
3531 b'',
3531 b'',
3532 ),
3532 ),
3533 ]
3533 ]
3534 )
3534 )
3535 for name, fmt, data, cond, extra_label in cols:
3535 for name, fmt, data, cond, extra_label in cols:
3536 if cond:
3536 if cond:
3537 fm.plain(sep, label=b'grep.sep')
3537 fm.plain(sep, label=b'grep.sep')
3538 field = fieldnamemap.get(name, name)
3538 field = fieldnamemap.get(name, name)
3539 label = extra_label + (b'grep.%s' % name)
3539 label = extra_label + (b'grep.%s' % name)
3540 fm.condwrite(cond, field, fmt, data, label=label)
3540 fm.condwrite(cond, field, fmt, data, label=label)
3541 if not opts.get(b'files_with_matches'):
3541 if not opts.get(b'files_with_matches'):
3542 fm.plain(sep, label=b'grep.sep')
3542 fm.plain(sep, label=b'grep.sep')
3543 if not opts.get(b'text') and binary():
3543 if not opts.get(b'text') and binary():
3544 fm.plain(_(b" Binary file matches"))
3544 fm.plain(_(b" Binary file matches"))
3545 else:
3545 else:
3546 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3546 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3547 fm.plain(eol)
3547 fm.plain(eol)
3548 found = True
3548 found = True
3549 if opts.get(b'files_with_matches'):
3549 if opts.get(b'files_with_matches'):
3550 break
3550 break
3551 return found
3551 return found
3552
3552
3553 def displaymatches(fm, l):
3553 def displaymatches(fm, l):
3554 p = 0
3554 p = 0
3555 for s, e in l.findpos():
3555 for s, e in l.findpos():
3556 if p < s:
3556 if p < s:
3557 fm.startitem()
3557 fm.startitem()
3558 fm.write(b'text', b'%s', l.line[p:s])
3558 fm.write(b'text', b'%s', l.line[p:s])
3559 fm.data(matched=False)
3559 fm.data(matched=False)
3560 fm.startitem()
3560 fm.startitem()
3561 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3561 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3562 fm.data(matched=True)
3562 fm.data(matched=True)
3563 p = e
3563 p = e
3564 if p < len(l.line):
3564 if p < len(l.line):
3565 fm.startitem()
3565 fm.startitem()
3566 fm.write(b'text', b'%s', l.line[p:])
3566 fm.write(b'text', b'%s', l.line[p:])
3567 fm.data(matched=False)
3567 fm.data(matched=False)
3568 fm.end()
3568 fm.end()
3569
3569
3570 skip = set()
3570 skip = set()
3571 revfiles = {}
3571 revfiles = {}
3572 match = scmutil.match(repo[None], pats, opts)
3572 match = scmutil.match(repo[None], pats, opts)
3573 found = False
3573 found = False
3574 follow = opts.get(b'follow')
3574 follow = opts.get(b'follow')
3575
3575
3576 getrenamed = scmutil.getrenamedfn(repo)
3576 getrenamed = scmutil.getrenamedfn(repo)
3577
3577
3578 def get_file_content(filename, filelog, filenode, context, revision):
3578 def get_file_content(filename, filelog, filenode, context, revision):
3579 try:
3579 try:
3580 content = filelog.read(filenode)
3580 content = filelog.read(filenode)
3581 except error.WdirUnsupported:
3581 except error.WdirUnsupported:
3582 content = context[filename].data()
3582 content = context[filename].data()
3583 except error.CensoredNodeError:
3583 except error.CensoredNodeError:
3584 content = None
3584 content = None
3585 ui.warn(
3585 ui.warn(
3586 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3586 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3587 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3587 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3588 )
3588 )
3589 return content
3589 return content
3590
3590
3591 def prep(ctx, fns):
3591 def prep(ctx, fns):
3592 rev = ctx.rev()
3592 rev = ctx.rev()
3593 pctx = ctx.p1()
3593 pctx = ctx.p1()
3594 parent = pctx.rev()
3594 parent = pctx.rev()
3595 matches.setdefault(rev, {})
3595 matches.setdefault(rev, {})
3596 matches.setdefault(parent, {})
3596 matches.setdefault(parent, {})
3597 files = revfiles.setdefault(rev, [])
3597 files = revfiles.setdefault(rev, [])
3598 for fn in fns:
3598 for fn in fns:
3599 flog = getfile(fn)
3599 flog = getfile(fn)
3600 try:
3600 try:
3601 fnode = ctx.filenode(fn)
3601 fnode = ctx.filenode(fn)
3602 except error.LookupError:
3602 except error.LookupError:
3603 continue
3603 continue
3604
3604
3605 copy = None
3605 copy = None
3606 if follow:
3606 if follow:
3607 copy = getrenamed(fn, rev)
3607 copy = getrenamed(fn, rev)
3608 if copy:
3608 if copy:
3609 copies.setdefault(rev, {})[fn] = copy
3609 copies.setdefault(rev, {})[fn] = copy
3610 if fn in skip:
3610 if fn in skip:
3611 skip.add(copy)
3611 skip.add(copy)
3612 if fn in skip:
3612 if fn in skip:
3613 continue
3613 continue
3614 files.append(fn)
3614 files.append(fn)
3615
3615
3616 if fn not in matches[rev]:
3616 if fn not in matches[rev]:
3617 content = get_file_content(fn, flog, fnode, ctx, rev)
3617 content = get_file_content(fn, flog, fnode, ctx, rev)
3618 grepbody(fn, rev, content)
3618 grepbody(fn, rev, content)
3619
3619
3620 pfn = copy or fn
3620 pfn = copy or fn
3621 if pfn not in matches[parent]:
3621 if pfn not in matches[parent]:
3622 try:
3622 try:
3623 pfnode = pctx.filenode(pfn)
3623 pfnode = pctx.filenode(pfn)
3624 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3624 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3625 grepbody(pfn, parent, pcontent)
3625 grepbody(pfn, parent, pcontent)
3626 except error.LookupError:
3626 except error.LookupError:
3627 pass
3627 pass
3628
3628
3629 ui.pager(b'grep')
3629 ui.pager(b'grep')
3630 fm = ui.formatter(b'grep', opts)
3630 fm = ui.formatter(b'grep', opts)
3631 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3631 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3632 rev = ctx.rev()
3632 rev = ctx.rev()
3633 parent = ctx.p1().rev()
3633 parent = ctx.p1().rev()
3634 for fn in sorted(revfiles.get(rev, [])):
3634 for fn in sorted(revfiles.get(rev, [])):
3635 states = matches[rev][fn]
3635 states = matches[rev][fn]
3636 copy = copies.get(rev, {}).get(fn)
3636 copy = copies.get(rev, {}).get(fn)
3637 if fn in skip:
3637 if fn in skip:
3638 if copy:
3638 if copy:
3639 skip.add(copy)
3639 skip.add(copy)
3640 continue
3640 continue
3641 pstates = matches.get(parent, {}).get(copy or fn, [])
3641 pstates = matches.get(parent, {}).get(copy or fn, [])
3642 if pstates or states:
3642 if pstates or states:
3643 r = display(fm, fn, ctx, pstates, states)
3643 r = display(fm, fn, ctx, pstates, states)
3644 found = found or r
3644 found = found or r
3645 if r and not diff and not all_files:
3645 if r and not diff and not all_files:
3646 skip.add(fn)
3646 skip.add(fn)
3647 if copy:
3647 if copy:
3648 skip.add(copy)
3648 skip.add(copy)
3649 del revfiles[rev]
3649 del revfiles[rev]
3650 # We will keep the matches dict for the duration of the window
3650 # We will keep the matches dict for the duration of the window
3651 # clear the matches dict once the window is over
3651 # clear the matches dict once the window is over
3652 if not revfiles:
3652 if not revfiles:
3653 matches.clear()
3653 matches.clear()
3654 fm.end()
3654 fm.end()
3655
3655
3656 return not found
3656 return not found
3657
3657
3658
3658
3659 @command(
3659 @command(
3660 b'heads',
3660 b'heads',
3661 [
3661 [
3662 (
3662 (
3663 b'r',
3663 b'r',
3664 b'rev',
3664 b'rev',
3665 b'',
3665 b'',
3666 _(b'show only heads which are descendants of STARTREV'),
3666 _(b'show only heads which are descendants of STARTREV'),
3667 _(b'STARTREV'),
3667 _(b'STARTREV'),
3668 ),
3668 ),
3669 (b't', b'topo', False, _(b'show topological heads only')),
3669 (b't', b'topo', False, _(b'show topological heads only')),
3670 (
3670 (
3671 b'a',
3671 b'a',
3672 b'active',
3672 b'active',
3673 False,
3673 False,
3674 _(b'show active branchheads only (DEPRECATED)'),
3674 _(b'show active branchheads only (DEPRECATED)'),
3675 ),
3675 ),
3676 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3676 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3677 ]
3677 ]
3678 + templateopts,
3678 + templateopts,
3679 _(b'[-ct] [-r STARTREV] [REV]...'),
3679 _(b'[-ct] [-r STARTREV] [REV]...'),
3680 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3680 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3681 intents={INTENT_READONLY},
3681 intents={INTENT_READONLY},
3682 )
3682 )
3683 def heads(ui, repo, *branchrevs, **opts):
3683 def heads(ui, repo, *branchrevs, **opts):
3684 """show branch heads
3684 """show branch heads
3685
3685
3686 With no arguments, show all open branch heads in the repository.
3686 With no arguments, show all open branch heads in the repository.
3687 Branch heads are changesets that have no descendants on the
3687 Branch heads are changesets that have no descendants on the
3688 same branch. They are where development generally takes place and
3688 same branch. They are where development generally takes place and
3689 are the usual targets for update and merge operations.
3689 are the usual targets for update and merge operations.
3690
3690
3691 If one or more REVs are given, only open branch heads on the
3691 If one or more REVs are given, only open branch heads on the
3692 branches associated with the specified changesets are shown. This
3692 branches associated with the specified changesets are shown. This
3693 means that you can use :hg:`heads .` to see the heads on the
3693 means that you can use :hg:`heads .` to see the heads on the
3694 currently checked-out branch.
3694 currently checked-out branch.
3695
3695
3696 If -c/--closed is specified, also show branch heads marked closed
3696 If -c/--closed is specified, also show branch heads marked closed
3697 (see :hg:`commit --close-branch`).
3697 (see :hg:`commit --close-branch`).
3698
3698
3699 If STARTREV is specified, only those heads that are descendants of
3699 If STARTREV is specified, only those heads that are descendants of
3700 STARTREV will be displayed.
3700 STARTREV will be displayed.
3701
3701
3702 If -t/--topo is specified, named branch mechanics will be ignored and only
3702 If -t/--topo is specified, named branch mechanics will be ignored and only
3703 topological heads (changesets with no children) will be shown.
3703 topological heads (changesets with no children) will be shown.
3704
3704
3705 Returns 0 if matching heads are found, 1 if not.
3705 Returns 0 if matching heads are found, 1 if not.
3706 """
3706 """
3707
3707
3708 opts = pycompat.byteskwargs(opts)
3708 opts = pycompat.byteskwargs(opts)
3709 start = None
3709 start = None
3710 rev = opts.get(b'rev')
3710 rev = opts.get(b'rev')
3711 if rev:
3711 if rev:
3712 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3712 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3713 start = scmutil.revsingle(repo, rev, None).node()
3713 start = scmutil.revsingle(repo, rev, None).node()
3714
3714
3715 if opts.get(b'topo'):
3715 if opts.get(b'topo'):
3716 heads = [repo[h] for h in repo.heads(start)]
3716 heads = [repo[h] for h in repo.heads(start)]
3717 else:
3717 else:
3718 heads = []
3718 heads = []
3719 for branch in repo.branchmap():
3719 for branch in repo.branchmap():
3720 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3720 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3721 heads = [repo[h] for h in heads]
3721 heads = [repo[h] for h in heads]
3722
3722
3723 if branchrevs:
3723 if branchrevs:
3724 branches = {
3724 branches = {
3725 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3725 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3726 }
3726 }
3727 heads = [h for h in heads if h.branch() in branches]
3727 heads = [h for h in heads if h.branch() in branches]
3728
3728
3729 if opts.get(b'active') and branchrevs:
3729 if opts.get(b'active') and branchrevs:
3730 dagheads = repo.heads(start)
3730 dagheads = repo.heads(start)
3731 heads = [h for h in heads if h.node() in dagheads]
3731 heads = [h for h in heads if h.node() in dagheads]
3732
3732
3733 if branchrevs:
3733 if branchrevs:
3734 haveheads = {h.branch() for h in heads}
3734 haveheads = {h.branch() for h in heads}
3735 if branches - haveheads:
3735 if branches - haveheads:
3736 headless = b', '.join(b for b in branches - haveheads)
3736 headless = b', '.join(b for b in branches - haveheads)
3737 msg = _(b'no open branch heads found on branches %s')
3737 msg = _(b'no open branch heads found on branches %s')
3738 if opts.get(b'rev'):
3738 if opts.get(b'rev'):
3739 msg += _(b' (started at %s)') % opts[b'rev']
3739 msg += _(b' (started at %s)') % opts[b'rev']
3740 ui.warn((msg + b'\n') % headless)
3740 ui.warn((msg + b'\n') % headless)
3741
3741
3742 if not heads:
3742 if not heads:
3743 return 1
3743 return 1
3744
3744
3745 ui.pager(b'heads')
3745 ui.pager(b'heads')
3746 heads = sorted(heads, key=lambda x: -(x.rev()))
3746 heads = sorted(heads, key=lambda x: -(x.rev()))
3747 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3747 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3748 for ctx in heads:
3748 for ctx in heads:
3749 displayer.show(ctx)
3749 displayer.show(ctx)
3750 displayer.close()
3750 displayer.close()
3751
3751
3752
3752
3753 @command(
3753 @command(
3754 b'help',
3754 b'help',
3755 [
3755 [
3756 (b'e', b'extension', None, _(b'show only help for extensions')),
3756 (b'e', b'extension', None, _(b'show only help for extensions')),
3757 (b'c', b'command', None, _(b'show only help for commands')),
3757 (b'c', b'command', None, _(b'show only help for commands')),
3758 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3758 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3759 (
3759 (
3760 b's',
3760 b's',
3761 b'system',
3761 b'system',
3762 [],
3762 [],
3763 _(b'show help for specific platform(s)'),
3763 _(b'show help for specific platform(s)'),
3764 _(b'PLATFORM'),
3764 _(b'PLATFORM'),
3765 ),
3765 ),
3766 ],
3766 ],
3767 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3767 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3768 helpcategory=command.CATEGORY_HELP,
3768 helpcategory=command.CATEGORY_HELP,
3769 norepo=True,
3769 norepo=True,
3770 intents={INTENT_READONLY},
3770 intents={INTENT_READONLY},
3771 )
3771 )
3772 def help_(ui, name=None, **opts):
3772 def help_(ui, name=None, **opts):
3773 """show help for a given topic or a help overview
3773 """show help for a given topic or a help overview
3774
3774
3775 With no arguments, print a list of commands with short help messages.
3775 With no arguments, print a list of commands with short help messages.
3776
3776
3777 Given a topic, extension, or command name, print help for that
3777 Given a topic, extension, or command name, print help for that
3778 topic.
3778 topic.
3779
3779
3780 Returns 0 if successful.
3780 Returns 0 if successful.
3781 """
3781 """
3782
3782
3783 keep = opts.get('system') or []
3783 keep = opts.get('system') or []
3784 if len(keep) == 0:
3784 if len(keep) == 0:
3785 if pycompat.sysplatform.startswith(b'win'):
3785 if pycompat.sysplatform.startswith(b'win'):
3786 keep.append(b'windows')
3786 keep.append(b'windows')
3787 elif pycompat.sysplatform == b'OpenVMS':
3787 elif pycompat.sysplatform == b'OpenVMS':
3788 keep.append(b'vms')
3788 keep.append(b'vms')
3789 elif pycompat.sysplatform == b'plan9':
3789 elif pycompat.sysplatform == b'plan9':
3790 keep.append(b'plan9')
3790 keep.append(b'plan9')
3791 else:
3791 else:
3792 keep.append(b'unix')
3792 keep.append(b'unix')
3793 keep.append(pycompat.sysplatform.lower())
3793 keep.append(pycompat.sysplatform.lower())
3794 if ui.verbose:
3794 if ui.verbose:
3795 keep.append(b'verbose')
3795 keep.append(b'verbose')
3796
3796
3797 commands = sys.modules[__name__]
3797 commands = sys.modules[__name__]
3798 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3798 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3799 ui.pager(b'help')
3799 ui.pager(b'help')
3800 ui.write(formatted)
3800 ui.write(formatted)
3801
3801
3802
3802
3803 @command(
3803 @command(
3804 b'identify|id',
3804 b'identify|id',
3805 [
3805 [
3806 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3806 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3807 (b'n', b'num', None, _(b'show local revision number')),
3807 (b'n', b'num', None, _(b'show local revision number')),
3808 (b'i', b'id', None, _(b'show global revision id')),
3808 (b'i', b'id', None, _(b'show global revision id')),
3809 (b'b', b'branch', None, _(b'show branch')),
3809 (b'b', b'branch', None, _(b'show branch')),
3810 (b't', b'tags', None, _(b'show tags')),
3810 (b't', b'tags', None, _(b'show tags')),
3811 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3811 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3812 ]
3812 ]
3813 + remoteopts
3813 + remoteopts
3814 + formatteropts,
3814 + formatteropts,
3815 _(b'[-nibtB] [-r REV] [SOURCE]'),
3815 _(b'[-nibtB] [-r REV] [SOURCE]'),
3816 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3816 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3817 optionalrepo=True,
3817 optionalrepo=True,
3818 intents={INTENT_READONLY},
3818 intents={INTENT_READONLY},
3819 )
3819 )
3820 def identify(
3820 def identify(
3821 ui,
3821 ui,
3822 repo,
3822 repo,
3823 source=None,
3823 source=None,
3824 rev=None,
3824 rev=None,
3825 num=None,
3825 num=None,
3826 id=None,
3826 id=None,
3827 branch=None,
3827 branch=None,
3828 tags=None,
3828 tags=None,
3829 bookmarks=None,
3829 bookmarks=None,
3830 **opts
3830 **opts
3831 ):
3831 ):
3832 """identify the working directory or specified revision
3832 """identify the working directory or specified revision
3833
3833
3834 Print a summary identifying the repository state at REV using one or
3834 Print a summary identifying the repository state at REV using one or
3835 two parent hash identifiers, followed by a "+" if the working
3835 two parent hash identifiers, followed by a "+" if the working
3836 directory has uncommitted changes, the branch name (if not default),
3836 directory has uncommitted changes, the branch name (if not default),
3837 a list of tags, and a list of bookmarks.
3837 a list of tags, and a list of bookmarks.
3838
3838
3839 When REV is not given, print a summary of the current state of the
3839 When REV is not given, print a summary of the current state of the
3840 repository including the working directory. Specify -r. to get information
3840 repository including the working directory. Specify -r. to get information
3841 of the working directory parent without scanning uncommitted changes.
3841 of the working directory parent without scanning uncommitted changes.
3842
3842
3843 Specifying a path to a repository root or Mercurial bundle will
3843 Specifying a path to a repository root or Mercurial bundle will
3844 cause lookup to operate on that repository/bundle.
3844 cause lookup to operate on that repository/bundle.
3845
3845
3846 .. container:: verbose
3846 .. container:: verbose
3847
3847
3848 Template:
3848 Template:
3849
3849
3850 The following keywords are supported in addition to the common template
3850 The following keywords are supported in addition to the common template
3851 keywords and functions. See also :hg:`help templates`.
3851 keywords and functions. See also :hg:`help templates`.
3852
3852
3853 :dirty: String. Character ``+`` denoting if the working directory has
3853 :dirty: String. Character ``+`` denoting if the working directory has
3854 uncommitted changes.
3854 uncommitted changes.
3855 :id: String. One or two nodes, optionally followed by ``+``.
3855 :id: String. One or two nodes, optionally followed by ``+``.
3856 :parents: List of strings. Parent nodes of the changeset.
3856 :parents: List of strings. Parent nodes of the changeset.
3857
3857
3858 Examples:
3858 Examples:
3859
3859
3860 - generate a build identifier for the working directory::
3860 - generate a build identifier for the working directory::
3861
3861
3862 hg id --id > build-id.dat
3862 hg id --id > build-id.dat
3863
3863
3864 - find the revision corresponding to a tag::
3864 - find the revision corresponding to a tag::
3865
3865
3866 hg id -n -r 1.3
3866 hg id -n -r 1.3
3867
3867
3868 - check the most recent revision of a remote repository::
3868 - check the most recent revision of a remote repository::
3869
3869
3870 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3870 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3871
3871
3872 See :hg:`log` for generating more information about specific revisions,
3872 See :hg:`log` for generating more information about specific revisions,
3873 including full hash identifiers.
3873 including full hash identifiers.
3874
3874
3875 Returns 0 if successful.
3875 Returns 0 if successful.
3876 """
3876 """
3877
3877
3878 opts = pycompat.byteskwargs(opts)
3878 opts = pycompat.byteskwargs(opts)
3879 if not repo and not source:
3879 if not repo and not source:
3880 raise error.Abort(
3880 raise error.Abort(
3881 _(b"there is no Mercurial repository here (.hg not found)")
3881 _(b"there is no Mercurial repository here (.hg not found)")
3882 )
3882 )
3883
3883
3884 default = not (num or id or branch or tags or bookmarks)
3884 default = not (num or id or branch or tags or bookmarks)
3885 output = []
3885 output = []
3886 revs = []
3886 revs = []
3887
3887
3888 if source:
3888 if source:
3889 source, branches = hg.parseurl(ui.expandpath(source))
3889 source, branches = hg.parseurl(ui.expandpath(source))
3890 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3890 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3891 repo = peer.local()
3891 repo = peer.local()
3892 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3892 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3893
3893
3894 fm = ui.formatter(b'identify', opts)
3894 fm = ui.formatter(b'identify', opts)
3895 fm.startitem()
3895 fm.startitem()
3896
3896
3897 if not repo:
3897 if not repo:
3898 if num or branch or tags:
3898 if num or branch or tags:
3899 raise error.Abort(
3899 raise error.Abort(
3900 _(b"can't query remote revision number, branch, or tags")
3900 _(b"can't query remote revision number, branch, or tags")
3901 )
3901 )
3902 if not rev and revs:
3902 if not rev and revs:
3903 rev = revs[0]
3903 rev = revs[0]
3904 if not rev:
3904 if not rev:
3905 rev = b"tip"
3905 rev = b"tip"
3906
3906
3907 remoterev = peer.lookup(rev)
3907 remoterev = peer.lookup(rev)
3908 hexrev = fm.hexfunc(remoterev)
3908 hexrev = fm.hexfunc(remoterev)
3909 if default or id:
3909 if default or id:
3910 output = [hexrev]
3910 output = [hexrev]
3911 fm.data(id=hexrev)
3911 fm.data(id=hexrev)
3912
3912
3913 @util.cachefunc
3913 @util.cachefunc
3914 def getbms():
3914 def getbms():
3915 bms = []
3915 bms = []
3916
3916
3917 if b'bookmarks' in peer.listkeys(b'namespaces'):
3917 if b'bookmarks' in peer.listkeys(b'namespaces'):
3918 hexremoterev = hex(remoterev)
3918 hexremoterev = hex(remoterev)
3919 bms = [
3919 bms = [
3920 bm
3920 bm
3921 for bm, bmr in pycompat.iteritems(
3921 for bm, bmr in pycompat.iteritems(
3922 peer.listkeys(b'bookmarks')
3922 peer.listkeys(b'bookmarks')
3923 )
3923 )
3924 if bmr == hexremoterev
3924 if bmr == hexremoterev
3925 ]
3925 ]
3926
3926
3927 return sorted(bms)
3927 return sorted(bms)
3928
3928
3929 if fm.isplain():
3929 if fm.isplain():
3930 if bookmarks:
3930 if bookmarks:
3931 output.extend(getbms())
3931 output.extend(getbms())
3932 elif default and not ui.quiet:
3932 elif default and not ui.quiet:
3933 # multiple bookmarks for a single parent separated by '/'
3933 # multiple bookmarks for a single parent separated by '/'
3934 bm = b'/'.join(getbms())
3934 bm = b'/'.join(getbms())
3935 if bm:
3935 if bm:
3936 output.append(bm)
3936 output.append(bm)
3937 else:
3937 else:
3938 fm.data(node=hex(remoterev))
3938 fm.data(node=hex(remoterev))
3939 if bookmarks or b'bookmarks' in fm.datahint():
3939 if bookmarks or b'bookmarks' in fm.datahint():
3940 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3940 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3941 else:
3941 else:
3942 if rev:
3942 if rev:
3943 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3943 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3944 ctx = scmutil.revsingle(repo, rev, None)
3944 ctx = scmutil.revsingle(repo, rev, None)
3945
3945
3946 if ctx.rev() is None:
3946 if ctx.rev() is None:
3947 ctx = repo[None]
3947 ctx = repo[None]
3948 parents = ctx.parents()
3948 parents = ctx.parents()
3949 taglist = []
3949 taglist = []
3950 for p in parents:
3950 for p in parents:
3951 taglist.extend(p.tags())
3951 taglist.extend(p.tags())
3952
3952
3953 dirty = b""
3953 dirty = b""
3954 if ctx.dirty(missing=True, merge=False, branch=False):
3954 if ctx.dirty(missing=True, merge=False, branch=False):
3955 dirty = b'+'
3955 dirty = b'+'
3956 fm.data(dirty=dirty)
3956 fm.data(dirty=dirty)
3957
3957
3958 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3958 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3959 if default or id:
3959 if default or id:
3960 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3960 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3961 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3961 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3962
3962
3963 if num:
3963 if num:
3964 numoutput = [b"%d" % p.rev() for p in parents]
3964 numoutput = [b"%d" % p.rev() for p in parents]
3965 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3965 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3966
3966
3967 fm.data(
3967 fm.data(
3968 parents=fm.formatlist(
3968 parents=fm.formatlist(
3969 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3969 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3970 )
3970 )
3971 )
3971 )
3972 else:
3972 else:
3973 hexoutput = fm.hexfunc(ctx.node())
3973 hexoutput = fm.hexfunc(ctx.node())
3974 if default or id:
3974 if default or id:
3975 output = [hexoutput]
3975 output = [hexoutput]
3976 fm.data(id=hexoutput)
3976 fm.data(id=hexoutput)
3977
3977
3978 if num:
3978 if num:
3979 output.append(pycompat.bytestr(ctx.rev()))
3979 output.append(pycompat.bytestr(ctx.rev()))
3980 taglist = ctx.tags()
3980 taglist = ctx.tags()
3981
3981
3982 if default and not ui.quiet:
3982 if default and not ui.quiet:
3983 b = ctx.branch()
3983 b = ctx.branch()
3984 if b != b'default':
3984 if b != b'default':
3985 output.append(b"(%s)" % b)
3985 output.append(b"(%s)" % b)
3986
3986
3987 # multiple tags for a single parent separated by '/'
3987 # multiple tags for a single parent separated by '/'
3988 t = b'/'.join(taglist)
3988 t = b'/'.join(taglist)
3989 if t:
3989 if t:
3990 output.append(t)
3990 output.append(t)
3991
3991
3992 # multiple bookmarks for a single parent separated by '/'
3992 # multiple bookmarks for a single parent separated by '/'
3993 bm = b'/'.join(ctx.bookmarks())
3993 bm = b'/'.join(ctx.bookmarks())
3994 if bm:
3994 if bm:
3995 output.append(bm)
3995 output.append(bm)
3996 else:
3996 else:
3997 if branch:
3997 if branch:
3998 output.append(ctx.branch())
3998 output.append(ctx.branch())
3999
3999
4000 if tags:
4000 if tags:
4001 output.extend(taglist)
4001 output.extend(taglist)
4002
4002
4003 if bookmarks:
4003 if bookmarks:
4004 output.extend(ctx.bookmarks())
4004 output.extend(ctx.bookmarks())
4005
4005
4006 fm.data(node=ctx.hex())
4006 fm.data(node=ctx.hex())
4007 fm.data(branch=ctx.branch())
4007 fm.data(branch=ctx.branch())
4008 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4008 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4009 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4009 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4010 fm.context(ctx=ctx)
4010 fm.context(ctx=ctx)
4011
4011
4012 fm.plain(b"%s\n" % b' '.join(output))
4012 fm.plain(b"%s\n" % b' '.join(output))
4013 fm.end()
4013 fm.end()
4014
4014
4015
4015
4016 @command(
4016 @command(
4017 b'import|patch',
4017 b'import|patch',
4018 [
4018 [
4019 (
4019 (
4020 b'p',
4020 b'p',
4021 b'strip',
4021 b'strip',
4022 1,
4022 1,
4023 _(
4023 _(
4024 b'directory strip option for patch. This has the same '
4024 b'directory strip option for patch. This has the same '
4025 b'meaning as the corresponding patch option'
4025 b'meaning as the corresponding patch option'
4026 ),
4026 ),
4027 _(b'NUM'),
4027 _(b'NUM'),
4028 ),
4028 ),
4029 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4029 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4030 (b'', b'secret', None, _(b'use the secret phase for committing')),
4030 (b'', b'secret', None, _(b'use the secret phase for committing')),
4031 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4031 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4032 (
4032 (
4033 b'f',
4033 b'f',
4034 b'force',
4034 b'force',
4035 None,
4035 None,
4036 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4036 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4037 ),
4037 ),
4038 (
4038 (
4039 b'',
4039 b'',
4040 b'no-commit',
4040 b'no-commit',
4041 None,
4041 None,
4042 _(b"don't commit, just update the working directory"),
4042 _(b"don't commit, just update the working directory"),
4043 ),
4043 ),
4044 (
4044 (
4045 b'',
4045 b'',
4046 b'bypass',
4046 b'bypass',
4047 None,
4047 None,
4048 _(b"apply patch without touching the working directory"),
4048 _(b"apply patch without touching the working directory"),
4049 ),
4049 ),
4050 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4050 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4051 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4051 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4052 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4052 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4053 (
4053 (
4054 b'',
4054 b'',
4055 b'import-branch',
4055 b'import-branch',
4056 None,
4056 None,
4057 _(b'use any branch information in patch (implied by --exact)'),
4057 _(b'use any branch information in patch (implied by --exact)'),
4058 ),
4058 ),
4059 ]
4059 ]
4060 + commitopts
4060 + commitopts
4061 + commitopts2
4061 + commitopts2
4062 + similarityopts,
4062 + similarityopts,
4063 _(b'[OPTION]... PATCH...'),
4063 _(b'[OPTION]... PATCH...'),
4064 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4064 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4065 )
4065 )
4066 def import_(ui, repo, patch1=None, *patches, **opts):
4066 def import_(ui, repo, patch1=None, *patches, **opts):
4067 """import an ordered set of patches
4067 """import an ordered set of patches
4068
4068
4069 Import a list of patches and commit them individually (unless
4069 Import a list of patches and commit them individually (unless
4070 --no-commit is specified).
4070 --no-commit is specified).
4071
4071
4072 To read a patch from standard input (stdin), use "-" as the patch
4072 To read a patch from standard input (stdin), use "-" as the patch
4073 name. If a URL is specified, the patch will be downloaded from
4073 name. If a URL is specified, the patch will be downloaded from
4074 there.
4074 there.
4075
4075
4076 Import first applies changes to the working directory (unless
4076 Import first applies changes to the working directory (unless
4077 --bypass is specified), import will abort if there are outstanding
4077 --bypass is specified), import will abort if there are outstanding
4078 changes.
4078 changes.
4079
4079
4080 Use --bypass to apply and commit patches directly to the
4080 Use --bypass to apply and commit patches directly to the
4081 repository, without affecting the working directory. Without
4081 repository, without affecting the working directory. Without
4082 --exact, patches will be applied on top of the working directory
4082 --exact, patches will be applied on top of the working directory
4083 parent revision.
4083 parent revision.
4084
4084
4085 You can import a patch straight from a mail message. Even patches
4085 You can import a patch straight from a mail message. Even patches
4086 as attachments work (to use the body part, it must have type
4086 as attachments work (to use the body part, it must have type
4087 text/plain or text/x-patch). From and Subject headers of email
4087 text/plain or text/x-patch). From and Subject headers of email
4088 message are used as default committer and commit message. All
4088 message are used as default committer and commit message. All
4089 text/plain body parts before first diff are added to the commit
4089 text/plain body parts before first diff are added to the commit
4090 message.
4090 message.
4091
4091
4092 If the imported patch was generated by :hg:`export`, user and
4092 If the imported patch was generated by :hg:`export`, user and
4093 description from patch override values from message headers and
4093 description from patch override values from message headers and
4094 body. Values given on command line with -m/--message and -u/--user
4094 body. Values given on command line with -m/--message and -u/--user
4095 override these.
4095 override these.
4096
4096
4097 If --exact is specified, import will set the working directory to
4097 If --exact is specified, import will set the working directory to
4098 the parent of each patch before applying it, and will abort if the
4098 the parent of each patch before applying it, and will abort if the
4099 resulting changeset has a different ID than the one recorded in
4099 resulting changeset has a different ID than the one recorded in
4100 the patch. This will guard against various ways that portable
4100 the patch. This will guard against various ways that portable
4101 patch formats and mail systems might fail to transfer Mercurial
4101 patch formats and mail systems might fail to transfer Mercurial
4102 data or metadata. See :hg:`bundle` for lossless transmission.
4102 data or metadata. See :hg:`bundle` for lossless transmission.
4103
4103
4104 Use --partial to ensure a changeset will be created from the patch
4104 Use --partial to ensure a changeset will be created from the patch
4105 even if some hunks fail to apply. Hunks that fail to apply will be
4105 even if some hunks fail to apply. Hunks that fail to apply will be
4106 written to a <target-file>.rej file. Conflicts can then be resolved
4106 written to a <target-file>.rej file. Conflicts can then be resolved
4107 by hand before :hg:`commit --amend` is run to update the created
4107 by hand before :hg:`commit --amend` is run to update the created
4108 changeset. This flag exists to let people import patches that
4108 changeset. This flag exists to let people import patches that
4109 partially apply without losing the associated metadata (author,
4109 partially apply without losing the associated metadata (author,
4110 date, description, ...).
4110 date, description, ...).
4111
4111
4112 .. note::
4112 .. note::
4113
4113
4114 When no hunks apply cleanly, :hg:`import --partial` will create
4114 When no hunks apply cleanly, :hg:`import --partial` will create
4115 an empty changeset, importing only the patch metadata.
4115 an empty changeset, importing only the patch metadata.
4116
4116
4117 With -s/--similarity, hg will attempt to discover renames and
4117 With -s/--similarity, hg will attempt to discover renames and
4118 copies in the patch in the same way as :hg:`addremove`.
4118 copies in the patch in the same way as :hg:`addremove`.
4119
4119
4120 It is possible to use external patch programs to perform the patch
4120 It is possible to use external patch programs to perform the patch
4121 by setting the ``ui.patch`` configuration option. For the default
4121 by setting the ``ui.patch`` configuration option. For the default
4122 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4122 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4123 See :hg:`help config` for more information about configuration
4123 See :hg:`help config` for more information about configuration
4124 files and how to use these options.
4124 files and how to use these options.
4125
4125
4126 See :hg:`help dates` for a list of formats valid for -d/--date.
4126 See :hg:`help dates` for a list of formats valid for -d/--date.
4127
4127
4128 .. container:: verbose
4128 .. container:: verbose
4129
4129
4130 Examples:
4130 Examples:
4131
4131
4132 - import a traditional patch from a website and detect renames::
4132 - import a traditional patch from a website and detect renames::
4133
4133
4134 hg import -s 80 http://example.com/bugfix.patch
4134 hg import -s 80 http://example.com/bugfix.patch
4135
4135
4136 - import a changeset from an hgweb server::
4136 - import a changeset from an hgweb server::
4137
4137
4138 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4138 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4139
4139
4140 - import all the patches in an Unix-style mbox::
4140 - import all the patches in an Unix-style mbox::
4141
4141
4142 hg import incoming-patches.mbox
4142 hg import incoming-patches.mbox
4143
4143
4144 - import patches from stdin::
4144 - import patches from stdin::
4145
4145
4146 hg import -
4146 hg import -
4147
4147
4148 - attempt to exactly restore an exported changeset (not always
4148 - attempt to exactly restore an exported changeset (not always
4149 possible)::
4149 possible)::
4150
4150
4151 hg import --exact proposed-fix.patch
4151 hg import --exact proposed-fix.patch
4152
4152
4153 - use an external tool to apply a patch which is too fuzzy for
4153 - use an external tool to apply a patch which is too fuzzy for
4154 the default internal tool.
4154 the default internal tool.
4155
4155
4156 hg import --config ui.patch="patch --merge" fuzzy.patch
4156 hg import --config ui.patch="patch --merge" fuzzy.patch
4157
4157
4158 - change the default fuzzing from 2 to a less strict 7
4158 - change the default fuzzing from 2 to a less strict 7
4159
4159
4160 hg import --config ui.fuzz=7 fuzz.patch
4160 hg import --config ui.fuzz=7 fuzz.patch
4161
4161
4162 Returns 0 on success, 1 on partial success (see --partial).
4162 Returns 0 on success, 1 on partial success (see --partial).
4163 """
4163 """
4164
4164
4165 opts = pycompat.byteskwargs(opts)
4165 opts = pycompat.byteskwargs(opts)
4166 if not patch1:
4166 if not patch1:
4167 raise error.Abort(_(b'need at least one patch to import'))
4167 raise error.Abort(_(b'need at least one patch to import'))
4168
4168
4169 patches = (patch1,) + patches
4169 patches = (patch1,) + patches
4170
4170
4171 date = opts.get(b'date')
4171 date = opts.get(b'date')
4172 if date:
4172 if date:
4173 opts[b'date'] = dateutil.parsedate(date)
4173 opts[b'date'] = dateutil.parsedate(date)
4174
4174
4175 exact = opts.get(b'exact')
4175 exact = opts.get(b'exact')
4176 update = not opts.get(b'bypass')
4176 update = not opts.get(b'bypass')
4177 if not update and opts.get(b'no_commit'):
4177 if not update and opts.get(b'no_commit'):
4178 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4178 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4179 if opts.get(b'secret') and opts.get(b'no_commit'):
4179 if opts.get(b'secret') and opts.get(b'no_commit'):
4180 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4180 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4181 try:
4181 try:
4182 sim = float(opts.get(b'similarity') or 0)
4182 sim = float(opts.get(b'similarity') or 0)
4183 except ValueError:
4183 except ValueError:
4184 raise error.Abort(_(b'similarity must be a number'))
4184 raise error.Abort(_(b'similarity must be a number'))
4185 if sim < 0 or sim > 100:
4185 if sim < 0 or sim > 100:
4186 raise error.Abort(_(b'similarity must be between 0 and 100'))
4186 raise error.Abort(_(b'similarity must be between 0 and 100'))
4187 if sim and not update:
4187 if sim and not update:
4188 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4188 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4189 if exact:
4189 if exact:
4190 if opts.get(b'edit'):
4190 if opts.get(b'edit'):
4191 raise error.Abort(_(b'cannot use --exact with --edit'))
4191 raise error.Abort(_(b'cannot use --exact with --edit'))
4192 if opts.get(b'prefix'):
4192 if opts.get(b'prefix'):
4193 raise error.Abort(_(b'cannot use --exact with --prefix'))
4193 raise error.Abort(_(b'cannot use --exact with --prefix'))
4194
4194
4195 base = opts[b"base"]
4195 base = opts[b"base"]
4196 msgs = []
4196 msgs = []
4197 ret = 0
4197 ret = 0
4198
4198
4199 with repo.wlock():
4199 with repo.wlock():
4200 if update:
4200 if update:
4201 cmdutil.checkunfinished(repo)
4201 cmdutil.checkunfinished(repo)
4202 if exact or not opts.get(b'force'):
4202 if exact or not opts.get(b'force'):
4203 cmdutil.bailifchanged(repo)
4203 cmdutil.bailifchanged(repo)
4204
4204
4205 if not opts.get(b'no_commit'):
4205 if not opts.get(b'no_commit'):
4206 lock = repo.lock
4206 lock = repo.lock
4207 tr = lambda: repo.transaction(b'import')
4207 tr = lambda: repo.transaction(b'import')
4208 dsguard = util.nullcontextmanager
4208 dsguard = util.nullcontextmanager
4209 else:
4209 else:
4210 lock = util.nullcontextmanager
4210 lock = util.nullcontextmanager
4211 tr = util.nullcontextmanager
4211 tr = util.nullcontextmanager
4212 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4212 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4213 with lock(), tr(), dsguard():
4213 with lock(), tr(), dsguard():
4214 parents = repo[None].parents()
4214 parents = repo[None].parents()
4215 for patchurl in patches:
4215 for patchurl in patches:
4216 if patchurl == b'-':
4216 if patchurl == b'-':
4217 ui.status(_(b'applying patch from stdin\n'))
4217 ui.status(_(b'applying patch from stdin\n'))
4218 patchfile = ui.fin
4218 patchfile = ui.fin
4219 patchurl = b'stdin' # for error message
4219 patchurl = b'stdin' # for error message
4220 else:
4220 else:
4221 patchurl = os.path.join(base, patchurl)
4221 patchurl = os.path.join(base, patchurl)
4222 ui.status(_(b'applying %s\n') % patchurl)
4222 ui.status(_(b'applying %s\n') % patchurl)
4223 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4223 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4224
4224
4225 haspatch = False
4225 haspatch = False
4226 for hunk in patch.split(patchfile):
4226 for hunk in patch.split(patchfile):
4227 with patch.extract(ui, hunk) as patchdata:
4227 with patch.extract(ui, hunk) as patchdata:
4228 msg, node, rej = cmdutil.tryimportone(
4228 msg, node, rej = cmdutil.tryimportone(
4229 ui, repo, patchdata, parents, opts, msgs, hg.clean
4229 ui, repo, patchdata, parents, opts, msgs, hg.clean
4230 )
4230 )
4231 if msg:
4231 if msg:
4232 haspatch = True
4232 haspatch = True
4233 ui.note(msg + b'\n')
4233 ui.note(msg + b'\n')
4234 if update or exact:
4234 if update or exact:
4235 parents = repo[None].parents()
4235 parents = repo[None].parents()
4236 else:
4236 else:
4237 parents = [repo[node]]
4237 parents = [repo[node]]
4238 if rej:
4238 if rej:
4239 ui.write_err(_(b"patch applied partially\n"))
4239 ui.write_err(_(b"patch applied partially\n"))
4240 ui.write_err(
4240 ui.write_err(
4241 _(
4241 _(
4242 b"(fix the .rej files and run "
4242 b"(fix the .rej files and run "
4243 b"`hg commit --amend`)\n"
4243 b"`hg commit --amend`)\n"
4244 )
4244 )
4245 )
4245 )
4246 ret = 1
4246 ret = 1
4247 break
4247 break
4248
4248
4249 if not haspatch:
4249 if not haspatch:
4250 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4250 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4251
4251
4252 if msgs:
4252 if msgs:
4253 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4253 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4254 return ret
4254 return ret
4255
4255
4256
4256
4257 @command(
4257 @command(
4258 b'incoming|in',
4258 b'incoming|in',
4259 [
4259 [
4260 (
4260 (
4261 b'f',
4261 b'f',
4262 b'force',
4262 b'force',
4263 None,
4263 None,
4264 _(b'run even if remote repository is unrelated'),
4264 _(b'run even if remote repository is unrelated'),
4265 ),
4265 ),
4266 (b'n', b'newest-first', None, _(b'show newest record first')),
4266 (b'n', b'newest-first', None, _(b'show newest record first')),
4267 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4267 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4268 (
4268 (
4269 b'r',
4269 b'r',
4270 b'rev',
4270 b'rev',
4271 [],
4271 [],
4272 _(b'a remote changeset intended to be added'),
4272 _(b'a remote changeset intended to be added'),
4273 _(b'REV'),
4273 _(b'REV'),
4274 ),
4274 ),
4275 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4275 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4276 (
4276 (
4277 b'b',
4277 b'b',
4278 b'branch',
4278 b'branch',
4279 [],
4279 [],
4280 _(b'a specific branch you would like to pull'),
4280 _(b'a specific branch you would like to pull'),
4281 _(b'BRANCH'),
4281 _(b'BRANCH'),
4282 ),
4282 ),
4283 ]
4283 ]
4284 + logopts
4284 + logopts
4285 + remoteopts
4285 + remoteopts
4286 + subrepoopts,
4286 + subrepoopts,
4287 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4287 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4288 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4288 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4289 )
4289 )
4290 def incoming(ui, repo, source=b"default", **opts):
4290 def incoming(ui, repo, source=b"default", **opts):
4291 """show new changesets found in source
4291 """show new changesets found in source
4292
4292
4293 Show new changesets found in the specified path/URL or the default
4293 Show new changesets found in the specified path/URL or the default
4294 pull location. These are the changesets that would have been pulled
4294 pull location. These are the changesets that would have been pulled
4295 by :hg:`pull` at the time you issued this command.
4295 by :hg:`pull` at the time you issued this command.
4296
4296
4297 See pull for valid source format details.
4297 See pull for valid source format details.
4298
4298
4299 .. container:: verbose
4299 .. container:: verbose
4300
4300
4301 With -B/--bookmarks, the result of bookmark comparison between
4301 With -B/--bookmarks, the result of bookmark comparison between
4302 local and remote repositories is displayed. With -v/--verbose,
4302 local and remote repositories is displayed. With -v/--verbose,
4303 status is also displayed for each bookmark like below::
4303 status is also displayed for each bookmark like below::
4304
4304
4305 BM1 01234567890a added
4305 BM1 01234567890a added
4306 BM2 1234567890ab advanced
4306 BM2 1234567890ab advanced
4307 BM3 234567890abc diverged
4307 BM3 234567890abc diverged
4308 BM4 34567890abcd changed
4308 BM4 34567890abcd changed
4309
4309
4310 The action taken locally when pulling depends on the
4310 The action taken locally when pulling depends on the
4311 status of each bookmark:
4311 status of each bookmark:
4312
4312
4313 :``added``: pull will create it
4313 :``added``: pull will create it
4314 :``advanced``: pull will update it
4314 :``advanced``: pull will update it
4315 :``diverged``: pull will create a divergent bookmark
4315 :``diverged``: pull will create a divergent bookmark
4316 :``changed``: result depends on remote changesets
4316 :``changed``: result depends on remote changesets
4317
4317
4318 From the point of view of pulling behavior, bookmark
4318 From the point of view of pulling behavior, bookmark
4319 existing only in the remote repository are treated as ``added``,
4319 existing only in the remote repository are treated as ``added``,
4320 even if it is in fact locally deleted.
4320 even if it is in fact locally deleted.
4321
4321
4322 .. container:: verbose
4322 .. container:: verbose
4323
4323
4324 For remote repository, using --bundle avoids downloading the
4324 For remote repository, using --bundle avoids downloading the
4325 changesets twice if the incoming is followed by a pull.
4325 changesets twice if the incoming is followed by a pull.
4326
4326
4327 Examples:
4327 Examples:
4328
4328
4329 - show incoming changes with patches and full description::
4329 - show incoming changes with patches and full description::
4330
4330
4331 hg incoming -vp
4331 hg incoming -vp
4332
4332
4333 - show incoming changes excluding merges, store a bundle::
4333 - show incoming changes excluding merges, store a bundle::
4334
4334
4335 hg in -vpM --bundle incoming.hg
4335 hg in -vpM --bundle incoming.hg
4336 hg pull incoming.hg
4336 hg pull incoming.hg
4337
4337
4338 - briefly list changes inside a bundle::
4338 - briefly list changes inside a bundle::
4339
4339
4340 hg in changes.hg -T "{desc|firstline}\\n"
4340 hg in changes.hg -T "{desc|firstline}\\n"
4341
4341
4342 Returns 0 if there are incoming changes, 1 otherwise.
4342 Returns 0 if there are incoming changes, 1 otherwise.
4343 """
4343 """
4344 opts = pycompat.byteskwargs(opts)
4344 opts = pycompat.byteskwargs(opts)
4345 if opts.get(b'graph'):
4345 if opts.get(b'graph'):
4346 logcmdutil.checkunsupportedgraphflags([], opts)
4346 logcmdutil.checkunsupportedgraphflags([], opts)
4347
4347
4348 def display(other, chlist, displayer):
4348 def display(other, chlist, displayer):
4349 revdag = logcmdutil.graphrevs(other, chlist, opts)
4349 revdag = logcmdutil.graphrevs(other, chlist, opts)
4350 logcmdutil.displaygraph(
4350 logcmdutil.displaygraph(
4351 ui, repo, revdag, displayer, graphmod.asciiedges
4351 ui, repo, revdag, displayer, graphmod.asciiedges
4352 )
4352 )
4353
4353
4354 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4354 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4355 return 0
4355 return 0
4356
4356
4357 if opts.get(b'bundle') and opts.get(b'subrepos'):
4357 if opts.get(b'bundle') and opts.get(b'subrepos'):
4358 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4358 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4359
4359
4360 if opts.get(b'bookmarks'):
4360 if opts.get(b'bookmarks'):
4361 source, branches = hg.parseurl(
4361 source, branches = hg.parseurl(
4362 ui.expandpath(source), opts.get(b'branch')
4362 ui.expandpath(source), opts.get(b'branch')
4363 )
4363 )
4364 other = hg.peer(repo, opts, source)
4364 other = hg.peer(repo, opts, source)
4365 if b'bookmarks' not in other.listkeys(b'namespaces'):
4365 if b'bookmarks' not in other.listkeys(b'namespaces'):
4366 ui.warn(_(b"remote doesn't support bookmarks\n"))
4366 ui.warn(_(b"remote doesn't support bookmarks\n"))
4367 return 0
4367 return 0
4368 ui.pager(b'incoming')
4368 ui.pager(b'incoming')
4369 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4369 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4370 return bookmarks.incoming(ui, repo, other)
4370 return bookmarks.incoming(ui, repo, other)
4371
4371
4372 repo._subtoppath = ui.expandpath(source)
4372 repo._subtoppath = ui.expandpath(source)
4373 try:
4373 try:
4374 return hg.incoming(ui, repo, source, opts)
4374 return hg.incoming(ui, repo, source, opts)
4375 finally:
4375 finally:
4376 del repo._subtoppath
4376 del repo._subtoppath
4377
4377
4378
4378
4379 @command(
4379 @command(
4380 b'init',
4380 b'init',
4381 remoteopts,
4381 remoteopts,
4382 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4382 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4383 helpcategory=command.CATEGORY_REPO_CREATION,
4383 helpcategory=command.CATEGORY_REPO_CREATION,
4384 helpbasic=True,
4384 helpbasic=True,
4385 norepo=True,
4385 norepo=True,
4386 )
4386 )
4387 def init(ui, dest=b".", **opts):
4387 def init(ui, dest=b".", **opts):
4388 """create a new repository in the given directory
4388 """create a new repository in the given directory
4389
4389
4390 Initialize a new repository in the given directory. If the given
4390 Initialize a new repository in the given directory. If the given
4391 directory does not exist, it will be created.
4391 directory does not exist, it will be created.
4392
4392
4393 If no directory is given, the current directory is used.
4393 If no directory is given, the current directory is used.
4394
4394
4395 It is possible to specify an ``ssh://`` URL as the destination.
4395 It is possible to specify an ``ssh://`` URL as the destination.
4396 See :hg:`help urls` for more information.
4396 See :hg:`help urls` for more information.
4397
4397
4398 Returns 0 on success.
4398 Returns 0 on success.
4399 """
4399 """
4400 opts = pycompat.byteskwargs(opts)
4400 opts = pycompat.byteskwargs(opts)
4401 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4401 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4402
4402
4403
4403
4404 @command(
4404 @command(
4405 b'locate',
4405 b'locate',
4406 [
4406 [
4407 (
4407 (
4408 b'r',
4408 b'r',
4409 b'rev',
4409 b'rev',
4410 b'',
4410 b'',
4411 _(b'search the repository as it is in REV'),
4411 _(b'search the repository as it is in REV'),
4412 _(b'REV'),
4412 _(b'REV'),
4413 ),
4413 ),
4414 (
4414 (
4415 b'0',
4415 b'0',
4416 b'print0',
4416 b'print0',
4417 None,
4417 None,
4418 _(b'end filenames with NUL, for use with xargs'),
4418 _(b'end filenames with NUL, for use with xargs'),
4419 ),
4419 ),
4420 (
4420 (
4421 b'f',
4421 b'f',
4422 b'fullpath',
4422 b'fullpath',
4423 None,
4423 None,
4424 _(b'print complete paths from the filesystem root'),
4424 _(b'print complete paths from the filesystem root'),
4425 ),
4425 ),
4426 ]
4426 ]
4427 + walkopts,
4427 + walkopts,
4428 _(b'[OPTION]... [PATTERN]...'),
4428 _(b'[OPTION]... [PATTERN]...'),
4429 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4429 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4430 )
4430 )
4431 def locate(ui, repo, *pats, **opts):
4431 def locate(ui, repo, *pats, **opts):
4432 """locate files matching specific patterns (DEPRECATED)
4432 """locate files matching specific patterns (DEPRECATED)
4433
4433
4434 Print files under Mercurial control in the working directory whose
4434 Print files under Mercurial control in the working directory whose
4435 names match the given patterns.
4435 names match the given patterns.
4436
4436
4437 By default, this command searches all directories in the working
4437 By default, this command searches all directories in the working
4438 directory. To search just the current directory and its
4438 directory. To search just the current directory and its
4439 subdirectories, use "--include .".
4439 subdirectories, use "--include .".
4440
4440
4441 If no patterns are given to match, this command prints the names
4441 If no patterns are given to match, this command prints the names
4442 of all files under Mercurial control in the working directory.
4442 of all files under Mercurial control in the working directory.
4443
4443
4444 If you want to feed the output of this command into the "xargs"
4444 If you want to feed the output of this command into the "xargs"
4445 command, use the -0 option to both this command and "xargs". This
4445 command, use the -0 option to both this command and "xargs". This
4446 will avoid the problem of "xargs" treating single filenames that
4446 will avoid the problem of "xargs" treating single filenames that
4447 contain whitespace as multiple filenames.
4447 contain whitespace as multiple filenames.
4448
4448
4449 See :hg:`help files` for a more versatile command.
4449 See :hg:`help files` for a more versatile command.
4450
4450
4451 Returns 0 if a match is found, 1 otherwise.
4451 Returns 0 if a match is found, 1 otherwise.
4452 """
4452 """
4453 opts = pycompat.byteskwargs(opts)
4453 opts = pycompat.byteskwargs(opts)
4454 if opts.get(b'print0'):
4454 if opts.get(b'print0'):
4455 end = b'\0'
4455 end = b'\0'
4456 else:
4456 else:
4457 end = b'\n'
4457 end = b'\n'
4458 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4458 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4459
4459
4460 ret = 1
4460 ret = 1
4461 m = scmutil.match(
4461 m = scmutil.match(
4462 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4462 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4463 )
4463 )
4464
4464
4465 ui.pager(b'locate')
4465 ui.pager(b'locate')
4466 if ctx.rev() is None:
4466 if ctx.rev() is None:
4467 # When run on the working copy, "locate" includes removed files, so
4467 # When run on the working copy, "locate" includes removed files, so
4468 # we get the list of files from the dirstate.
4468 # we get the list of files from the dirstate.
4469 filesgen = sorted(repo.dirstate.matches(m))
4469 filesgen = sorted(repo.dirstate.matches(m))
4470 else:
4470 else:
4471 filesgen = ctx.matches(m)
4471 filesgen = ctx.matches(m)
4472 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4472 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4473 for abs in filesgen:
4473 for abs in filesgen:
4474 if opts.get(b'fullpath'):
4474 if opts.get(b'fullpath'):
4475 ui.write(repo.wjoin(abs), end)
4475 ui.write(repo.wjoin(abs), end)
4476 else:
4476 else:
4477 ui.write(uipathfn(abs), end)
4477 ui.write(uipathfn(abs), end)
4478 ret = 0
4478 ret = 0
4479
4479
4480 return ret
4480 return ret
4481
4481
4482
4482
4483 @command(
4483 @command(
4484 b'log|history',
4484 b'log|history',
4485 [
4485 [
4486 (
4486 (
4487 b'f',
4487 b'f',
4488 b'follow',
4488 b'follow',
4489 None,
4489 None,
4490 _(
4490 _(
4491 b'follow changeset history, or file history across copies and renames'
4491 b'follow changeset history, or file history across copies and renames'
4492 ),
4492 ),
4493 ),
4493 ),
4494 (
4494 (
4495 b'',
4495 b'',
4496 b'follow-first',
4496 b'follow-first',
4497 None,
4497 None,
4498 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4498 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4499 ),
4499 ),
4500 (
4500 (
4501 b'd',
4501 b'd',
4502 b'date',
4502 b'date',
4503 b'',
4503 b'',
4504 _(b'show revisions matching date spec'),
4504 _(b'show revisions matching date spec'),
4505 _(b'DATE'),
4505 _(b'DATE'),
4506 ),
4506 ),
4507 (b'C', b'copies', None, _(b'show copied files')),
4507 (b'C', b'copies', None, _(b'show copied files')),
4508 (
4508 (
4509 b'k',
4509 b'k',
4510 b'keyword',
4510 b'keyword',
4511 [],
4511 [],
4512 _(b'do case-insensitive search for a given text'),
4512 _(b'do case-insensitive search for a given text'),
4513 _(b'TEXT'),
4513 _(b'TEXT'),
4514 ),
4514 ),
4515 (
4515 (
4516 b'r',
4516 b'r',
4517 b'rev',
4517 b'rev',
4518 [],
4518 [],
4519 _(b'show the specified revision or revset'),
4519 _(b'show the specified revision or revset'),
4520 _(b'REV'),
4520 _(b'REV'),
4521 ),
4521 ),
4522 (
4522 (
4523 b'L',
4523 b'L',
4524 b'line-range',
4524 b'line-range',
4525 [],
4525 [],
4526 _(b'follow line range of specified file (EXPERIMENTAL)'),
4526 _(b'follow line range of specified file (EXPERIMENTAL)'),
4527 _(b'FILE,RANGE'),
4527 _(b'FILE,RANGE'),
4528 ),
4528 ),
4529 (
4529 (
4530 b'',
4530 b'',
4531 b'removed',
4531 b'removed',
4532 None,
4532 None,
4533 _(b'include revisions where files were removed'),
4533 _(b'include revisions where files were removed'),
4534 ),
4534 ),
4535 (
4535 (
4536 b'm',
4536 b'm',
4537 b'only-merges',
4537 b'only-merges',
4538 None,
4538 None,
4539 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4539 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4540 ),
4540 ),
4541 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4541 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4542 (
4542 (
4543 b'',
4543 b'',
4544 b'only-branch',
4544 b'only-branch',
4545 [],
4545 [],
4546 _(
4546 _(
4547 b'show only changesets within the given named branch (DEPRECATED)'
4547 b'show only changesets within the given named branch (DEPRECATED)'
4548 ),
4548 ),
4549 _(b'BRANCH'),
4549 _(b'BRANCH'),
4550 ),
4550 ),
4551 (
4551 (
4552 b'b',
4552 b'b',
4553 b'branch',
4553 b'branch',
4554 [],
4554 [],
4555 _(b'show changesets within the given named branch'),
4555 _(b'show changesets within the given named branch'),
4556 _(b'BRANCH'),
4556 _(b'BRANCH'),
4557 ),
4557 ),
4558 (
4558 (
4559 b'P',
4559 b'P',
4560 b'prune',
4560 b'prune',
4561 [],
4561 [],
4562 _(b'do not display revision or any of its ancestors'),
4562 _(b'do not display revision or any of its ancestors'),
4563 _(b'REV'),
4563 _(b'REV'),
4564 ),
4564 ),
4565 ]
4565 ]
4566 + logopts
4566 + logopts
4567 + walkopts,
4567 + walkopts,
4568 _(b'[OPTION]... [FILE]'),
4568 _(b'[OPTION]... [FILE]'),
4569 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4569 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4570 helpbasic=True,
4570 helpbasic=True,
4571 inferrepo=True,
4571 inferrepo=True,
4572 intents={INTENT_READONLY},
4572 intents={INTENT_READONLY},
4573 )
4573 )
4574 def log(ui, repo, *pats, **opts):
4574 def log(ui, repo, *pats, **opts):
4575 """show revision history of entire repository or files
4575 """show revision history of entire repository or files
4576
4576
4577 Print the revision history of the specified files or the entire
4577 Print the revision history of the specified files or the entire
4578 project.
4578 project.
4579
4579
4580 If no revision range is specified, the default is ``tip:0`` unless
4580 If no revision range is specified, the default is ``tip:0`` unless
4581 --follow is set, in which case the working directory parent is
4581 --follow is set, in which case the working directory parent is
4582 used as the starting revision.
4582 used as the starting revision.
4583
4583
4584 File history is shown without following rename or copy history of
4584 File history is shown without following rename or copy history of
4585 files. Use -f/--follow with a filename to follow history across
4585 files. Use -f/--follow with a filename to follow history across
4586 renames and copies. --follow without a filename will only show
4586 renames and copies. --follow without a filename will only show
4587 ancestors of the starting revision.
4587 ancestors of the starting revision.
4588
4588
4589 By default this command prints revision number and changeset id,
4589 By default this command prints revision number and changeset id,
4590 tags, non-trivial parents, user, date and time, and a summary for
4590 tags, non-trivial parents, user, date and time, and a summary for
4591 each commit. When the -v/--verbose switch is used, the list of
4591 each commit. When the -v/--verbose switch is used, the list of
4592 changed files and full commit message are shown.
4592 changed files and full commit message are shown.
4593
4593
4594 With --graph the revisions are shown as an ASCII art DAG with the most
4594 With --graph the revisions are shown as an ASCII art DAG with the most
4595 recent changeset at the top.
4595 recent changeset at the top.
4596 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4596 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4597 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4597 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4598 changeset from the lines below is a parent of the 'o' merge on the same
4598 changeset from the lines below is a parent of the 'o' merge on the same
4599 line.
4599 line.
4600 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4600 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4601 of a '|' indicates one or more revisions in a path are omitted.
4601 of a '|' indicates one or more revisions in a path are omitted.
4602
4602
4603 .. container:: verbose
4603 .. container:: verbose
4604
4604
4605 Use -L/--line-range FILE,M:N options to follow the history of lines
4605 Use -L/--line-range FILE,M:N options to follow the history of lines
4606 from M to N in FILE. With -p/--patch only diff hunks affecting
4606 from M to N in FILE. With -p/--patch only diff hunks affecting
4607 specified line range will be shown. This option requires --follow;
4607 specified line range will be shown. This option requires --follow;
4608 it can be specified multiple times. Currently, this option is not
4608 it can be specified multiple times. Currently, this option is not
4609 compatible with --graph. This option is experimental.
4609 compatible with --graph. This option is experimental.
4610
4610
4611 .. note::
4611 .. note::
4612
4612
4613 :hg:`log --patch` may generate unexpected diff output for merge
4613 :hg:`log --patch` may generate unexpected diff output for merge
4614 changesets, as it will only compare the merge changeset against
4614 changesets, as it will only compare the merge changeset against
4615 its first parent. Also, only files different from BOTH parents
4615 its first parent. Also, only files different from BOTH parents
4616 will appear in files:.
4616 will appear in files:.
4617
4617
4618 .. note::
4618 .. note::
4619
4619
4620 For performance reasons, :hg:`log FILE` may omit duplicate changes
4620 For performance reasons, :hg:`log FILE` may omit duplicate changes
4621 made on branches and will not show removals or mode changes. To
4621 made on branches and will not show removals or mode changes. To
4622 see all such changes, use the --removed switch.
4622 see all such changes, use the --removed switch.
4623
4623
4624 .. container:: verbose
4624 .. container:: verbose
4625
4625
4626 .. note::
4626 .. note::
4627
4627
4628 The history resulting from -L/--line-range options depends on diff
4628 The history resulting from -L/--line-range options depends on diff
4629 options; for instance if white-spaces are ignored, respective changes
4629 options; for instance if white-spaces are ignored, respective changes
4630 with only white-spaces in specified line range will not be listed.
4630 with only white-spaces in specified line range will not be listed.
4631
4631
4632 .. container:: verbose
4632 .. container:: verbose
4633
4633
4634 Some examples:
4634 Some examples:
4635
4635
4636 - changesets with full descriptions and file lists::
4636 - changesets with full descriptions and file lists::
4637
4637
4638 hg log -v
4638 hg log -v
4639
4639
4640 - changesets ancestral to the working directory::
4640 - changesets ancestral to the working directory::
4641
4641
4642 hg log -f
4642 hg log -f
4643
4643
4644 - last 10 commits on the current branch::
4644 - last 10 commits on the current branch::
4645
4645
4646 hg log -l 10 -b .
4646 hg log -l 10 -b .
4647
4647
4648 - changesets showing all modifications of a file, including removals::
4648 - changesets showing all modifications of a file, including removals::
4649
4649
4650 hg log --removed file.c
4650 hg log --removed file.c
4651
4651
4652 - all changesets that touch a directory, with diffs, excluding merges::
4652 - all changesets that touch a directory, with diffs, excluding merges::
4653
4653
4654 hg log -Mp lib/
4654 hg log -Mp lib/
4655
4655
4656 - all revision numbers that match a keyword::
4656 - all revision numbers that match a keyword::
4657
4657
4658 hg log -k bug --template "{rev}\\n"
4658 hg log -k bug --template "{rev}\\n"
4659
4659
4660 - the full hash identifier of the working directory parent::
4660 - the full hash identifier of the working directory parent::
4661
4661
4662 hg log -r . --template "{node}\\n"
4662 hg log -r . --template "{node}\\n"
4663
4663
4664 - list available log templates::
4664 - list available log templates::
4665
4665
4666 hg log -T list
4666 hg log -T list
4667
4667
4668 - check if a given changeset is included in a tagged release::
4668 - check if a given changeset is included in a tagged release::
4669
4669
4670 hg log -r "a21ccf and ancestor(1.9)"
4670 hg log -r "a21ccf and ancestor(1.9)"
4671
4671
4672 - find all changesets by some user in a date range::
4672 - find all changesets by some user in a date range::
4673
4673
4674 hg log -k alice -d "may 2008 to jul 2008"
4674 hg log -k alice -d "may 2008 to jul 2008"
4675
4675
4676 - summary of all changesets after the last tag::
4676 - summary of all changesets after the last tag::
4677
4677
4678 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4678 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4679
4679
4680 - changesets touching lines 13 to 23 for file.c::
4680 - changesets touching lines 13 to 23 for file.c::
4681
4681
4682 hg log -L file.c,13:23
4682 hg log -L file.c,13:23
4683
4683
4684 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4684 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4685 main.c with patch::
4685 main.c with patch::
4686
4686
4687 hg log -L file.c,13:23 -L main.c,2:6 -p
4687 hg log -L file.c,13:23 -L main.c,2:6 -p
4688
4688
4689 See :hg:`help dates` for a list of formats valid for -d/--date.
4689 See :hg:`help dates` for a list of formats valid for -d/--date.
4690
4690
4691 See :hg:`help revisions` for more about specifying and ordering
4691 See :hg:`help revisions` for more about specifying and ordering
4692 revisions.
4692 revisions.
4693
4693
4694 See :hg:`help templates` for more about pre-packaged styles and
4694 See :hg:`help templates` for more about pre-packaged styles and
4695 specifying custom templates. The default template used by the log
4695 specifying custom templates. The default template used by the log
4696 command can be customized via the ``ui.logtemplate`` configuration
4696 command can be customized via the ``ui.logtemplate`` configuration
4697 setting.
4697 setting.
4698
4698
4699 Returns 0 on success.
4699 Returns 0 on success.
4700
4700
4701 """
4701 """
4702 opts = pycompat.byteskwargs(opts)
4702 opts = pycompat.byteskwargs(opts)
4703 linerange = opts.get(b'line_range')
4703 linerange = opts.get(b'line_range')
4704
4704
4705 if linerange and not opts.get(b'follow'):
4705 if linerange and not opts.get(b'follow'):
4706 raise error.Abort(_(b'--line-range requires --follow'))
4706 raise error.Abort(_(b'--line-range requires --follow'))
4707
4707
4708 if linerange and pats:
4708 if linerange and pats:
4709 # TODO: take pats as patterns with no line-range filter
4709 # TODO: take pats as patterns with no line-range filter
4710 raise error.Abort(
4710 raise error.Abort(
4711 _(b'FILE arguments are not compatible with --line-range option')
4711 _(b'FILE arguments are not compatible with --line-range option')
4712 )
4712 )
4713
4713
4714 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4714 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4715 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4715 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4716 if linerange:
4716 if linerange:
4717 # TODO: should follow file history from logcmdutil._initialrevs(),
4717 # TODO: should follow file history from logcmdutil._initialrevs(),
4718 # then filter the result by logcmdutil._makerevset() and --limit
4718 # then filter the result by logcmdutil._makerevset() and --limit
4719 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4719 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4720
4720
4721 getcopies = None
4721 getcopies = None
4722 if opts.get(b'copies'):
4722 if opts.get(b'copies'):
4723 endrev = None
4723 endrev = None
4724 if revs:
4724 if revs:
4725 endrev = revs.max() + 1
4725 endrev = revs.max() + 1
4726 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4726 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4727
4727
4728 ui.pager(b'log')
4728 ui.pager(b'log')
4729 displayer = logcmdutil.changesetdisplayer(
4729 displayer = logcmdutil.changesetdisplayer(
4730 ui, repo, opts, differ, buffered=True
4730 ui, repo, opts, differ, buffered=True
4731 )
4731 )
4732 if opts.get(b'graph'):
4732 if opts.get(b'graph'):
4733 displayfn = logcmdutil.displaygraphrevs
4733 displayfn = logcmdutil.displaygraphrevs
4734 else:
4734 else:
4735 displayfn = logcmdutil.displayrevs
4735 displayfn = logcmdutil.displayrevs
4736 displayfn(ui, repo, revs, displayer, getcopies)
4736 displayfn(ui, repo, revs, displayer, getcopies)
4737
4737
4738
4738
4739 @command(
4739 @command(
4740 b'manifest',
4740 b'manifest',
4741 [
4741 [
4742 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4742 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4743 (b'', b'all', False, _(b"list files from all revisions")),
4743 (b'', b'all', False, _(b"list files from all revisions")),
4744 ]
4744 ]
4745 + formatteropts,
4745 + formatteropts,
4746 _(b'[-r REV]'),
4746 _(b'[-r REV]'),
4747 helpcategory=command.CATEGORY_MAINTENANCE,
4747 helpcategory=command.CATEGORY_MAINTENANCE,
4748 intents={INTENT_READONLY},
4748 intents={INTENT_READONLY},
4749 )
4749 )
4750 def manifest(ui, repo, node=None, rev=None, **opts):
4750 def manifest(ui, repo, node=None, rev=None, **opts):
4751 """output the current or given revision of the project manifest
4751 """output the current or given revision of the project manifest
4752
4752
4753 Print a list of version controlled files for the given revision.
4753 Print a list of version controlled files for the given revision.
4754 If no revision is given, the first parent of the working directory
4754 If no revision is given, the first parent of the working directory
4755 is used, or the null revision if no revision is checked out.
4755 is used, or the null revision if no revision is checked out.
4756
4756
4757 With -v, print file permissions, symlink and executable bits.
4757 With -v, print file permissions, symlink and executable bits.
4758 With --debug, print file revision hashes.
4758 With --debug, print file revision hashes.
4759
4759
4760 If option --all is specified, the list of all files from all revisions
4760 If option --all is specified, the list of all files from all revisions
4761 is printed. This includes deleted and renamed files.
4761 is printed. This includes deleted and renamed files.
4762
4762
4763 Returns 0 on success.
4763 Returns 0 on success.
4764 """
4764 """
4765 opts = pycompat.byteskwargs(opts)
4765 opts = pycompat.byteskwargs(opts)
4766 fm = ui.formatter(b'manifest', opts)
4766 fm = ui.formatter(b'manifest', opts)
4767
4767
4768 if opts.get(b'all'):
4768 if opts.get(b'all'):
4769 if rev or node:
4769 if rev or node:
4770 raise error.Abort(_(b"can't specify a revision with --all"))
4770 raise error.Abort(_(b"can't specify a revision with --all"))
4771
4771
4772 res = set()
4772 res = set()
4773 for rev in repo:
4773 for rev in repo:
4774 ctx = repo[rev]
4774 ctx = repo[rev]
4775 res |= set(ctx.files())
4775 res |= set(ctx.files())
4776
4776
4777 ui.pager(b'manifest')
4777 ui.pager(b'manifest')
4778 for f in sorted(res):
4778 for f in sorted(res):
4779 fm.startitem()
4779 fm.startitem()
4780 fm.write(b"path", b'%s\n', f)
4780 fm.write(b"path", b'%s\n', f)
4781 fm.end()
4781 fm.end()
4782 return
4782 return
4783
4783
4784 if rev and node:
4784 if rev and node:
4785 raise error.Abort(_(b"please specify just one revision"))
4785 raise error.Abort(_(b"please specify just one revision"))
4786
4786
4787 if not node:
4787 if not node:
4788 node = rev
4788 node = rev
4789
4789
4790 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4790 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4791 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4791 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4792 if node:
4792 if node:
4793 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4793 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4794 ctx = scmutil.revsingle(repo, node)
4794 ctx = scmutil.revsingle(repo, node)
4795 mf = ctx.manifest()
4795 mf = ctx.manifest()
4796 ui.pager(b'manifest')
4796 ui.pager(b'manifest')
4797 for f in ctx:
4797 for f in ctx:
4798 fm.startitem()
4798 fm.startitem()
4799 fm.context(ctx=ctx)
4799 fm.context(ctx=ctx)
4800 fl = ctx[f].flags()
4800 fl = ctx[f].flags()
4801 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4801 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4802 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4802 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4803 fm.write(b'path', b'%s\n', f)
4803 fm.write(b'path', b'%s\n', f)
4804 fm.end()
4804 fm.end()
4805
4805
4806
4806
4807 @command(
4807 @command(
4808 b'merge',
4808 b'merge',
4809 [
4809 [
4810 (
4810 (
4811 b'f',
4811 b'f',
4812 b'force',
4812 b'force',
4813 None,
4813 None,
4814 _(b'force a merge including outstanding changes (DEPRECATED)'),
4814 _(b'force a merge including outstanding changes (DEPRECATED)'),
4815 ),
4815 ),
4816 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4816 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4817 (
4817 (
4818 b'P',
4818 b'P',
4819 b'preview',
4819 b'preview',
4820 None,
4820 None,
4821 _(b'review revisions to merge (no merge is performed)'),
4821 _(b'review revisions to merge (no merge is performed)'),
4822 ),
4822 ),
4823 (b'', b'abort', None, _(b'abort the ongoing merge')),
4823 (b'', b'abort', None, _(b'abort the ongoing merge')),
4824 ]
4824 ]
4825 + mergetoolopts,
4825 + mergetoolopts,
4826 _(b'[-P] [[-r] REV]'),
4826 _(b'[-P] [[-r] REV]'),
4827 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4827 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4828 helpbasic=True,
4828 helpbasic=True,
4829 )
4829 )
4830 def merge(ui, repo, node=None, **opts):
4830 def merge(ui, repo, node=None, **opts):
4831 """merge another revision into working directory
4831 """merge another revision into working directory
4832
4832
4833 The current working directory is updated with all changes made in
4833 The current working directory is updated with all changes made in
4834 the requested revision since the last common predecessor revision.
4834 the requested revision since the last common predecessor revision.
4835
4835
4836 Files that changed between either parent are marked as changed for
4836 Files that changed between either parent are marked as changed for
4837 the next commit and a commit must be performed before any further
4837 the next commit and a commit must be performed before any further
4838 updates to the repository are allowed. The next commit will have
4838 updates to the repository are allowed. The next commit will have
4839 two parents.
4839 two parents.
4840
4840
4841 ``--tool`` can be used to specify the merge tool used for file
4841 ``--tool`` can be used to specify the merge tool used for file
4842 merges. It overrides the HGMERGE environment variable and your
4842 merges. It overrides the HGMERGE environment variable and your
4843 configuration files. See :hg:`help merge-tools` for options.
4843 configuration files. See :hg:`help merge-tools` for options.
4844
4844
4845 If no revision is specified, the working directory's parent is a
4845 If no revision is specified, the working directory's parent is a
4846 head revision, and the current branch contains exactly one other
4846 head revision, and the current branch contains exactly one other
4847 head, the other head is merged with by default. Otherwise, an
4847 head, the other head is merged with by default. Otherwise, an
4848 explicit revision with which to merge must be provided.
4848 explicit revision with which to merge must be provided.
4849
4849
4850 See :hg:`help resolve` for information on handling file conflicts.
4850 See :hg:`help resolve` for information on handling file conflicts.
4851
4851
4852 To undo an uncommitted merge, use :hg:`merge --abort` which
4852 To undo an uncommitted merge, use :hg:`merge --abort` which
4853 will check out a clean copy of the original merge parent, losing
4853 will check out a clean copy of the original merge parent, losing
4854 all changes.
4854 all changes.
4855
4855
4856 Returns 0 on success, 1 if there are unresolved files.
4856 Returns 0 on success, 1 if there are unresolved files.
4857 """
4857 """
4858
4858
4859 opts = pycompat.byteskwargs(opts)
4859 opts = pycompat.byteskwargs(opts)
4860 abort = opts.get(b'abort')
4860 abort = opts.get(b'abort')
4861 if abort and repo.dirstate.p2() == nullid:
4861 if abort and repo.dirstate.p2() == nullid:
4862 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4862 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4863 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4863 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4864 if abort:
4864 if abort:
4865 state = cmdutil.getunfinishedstate(repo)
4865 state = cmdutil.getunfinishedstate(repo)
4866 if state and state._opname != b'merge':
4866 if state and state._opname != b'merge':
4867 raise error.Abort(
4867 raise error.Abort(
4868 _(b'cannot abort merge with %s in progress') % (state._opname),
4868 _(b'cannot abort merge with %s in progress') % (state._opname),
4869 hint=state.hint(),
4869 hint=state.hint(),
4870 )
4870 )
4871 if node:
4871 if node:
4872 raise error.Abort(_(b"cannot specify a node with --abort"))
4872 raise error.Abort(_(b"cannot specify a node with --abort"))
4873 return hg.abortmerge(repo.ui, repo)
4873 return hg.abortmerge(repo.ui, repo)
4874
4874
4875 if opts.get(b'rev') and node:
4875 if opts.get(b'rev') and node:
4876 raise error.Abort(_(b"please specify just one revision"))
4876 raise error.Abort(_(b"please specify just one revision"))
4877 if not node:
4877 if not node:
4878 node = opts.get(b'rev')
4878 node = opts.get(b'rev')
4879
4879
4880 if node:
4880 if node:
4881 ctx = scmutil.revsingle(repo, node)
4881 ctx = scmutil.revsingle(repo, node)
4882 else:
4882 else:
4883 if ui.configbool(b'commands', b'merge.require-rev'):
4883 if ui.configbool(b'commands', b'merge.require-rev'):
4884 raise error.Abort(
4884 raise error.Abort(
4885 _(
4885 _(
4886 b'configuration requires specifying revision to merge '
4886 b'configuration requires specifying revision to merge '
4887 b'with'
4887 b'with'
4888 )
4888 )
4889 )
4889 )
4890 ctx = repo[destutil.destmerge(repo)]
4890 ctx = repo[destutil.destmerge(repo)]
4891
4891
4892 if ctx.node() is None:
4892 if ctx.node() is None:
4893 raise error.Abort(_(b'merging with the working copy has no effect'))
4893 raise error.Abort(_(b'merging with the working copy has no effect'))
4894
4894
4895 if opts.get(b'preview'):
4895 if opts.get(b'preview'):
4896 # find nodes that are ancestors of p2 but not of p1
4896 # find nodes that are ancestors of p2 but not of p1
4897 p1 = repo[b'.'].node()
4897 p1 = repo[b'.'].node()
4898 p2 = ctx.node()
4898 p2 = ctx.node()
4899 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4899 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4900
4900
4901 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4901 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4902 for node in nodes:
4902 for node in nodes:
4903 displayer.show(repo[node])
4903 displayer.show(repo[node])
4904 displayer.close()
4904 displayer.close()
4905 return 0
4905 return 0
4906
4906
4907 # ui.forcemerge is an internal variable, do not document
4907 # ui.forcemerge is an internal variable, do not document
4908 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4908 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4909 with ui.configoverride(overrides, b'merge'):
4909 with ui.configoverride(overrides, b'merge'):
4910 force = opts.get(b'force')
4910 force = opts.get(b'force')
4911 labels = [b'working copy', b'merge rev']
4911 labels = [b'working copy', b'merge rev']
4912 return hg.merge(ctx, force=force, labels=labels)
4912 return hg.merge(ctx, force=force, labels=labels)
4913
4913
4914
4914
4915 statemod.addunfinished(
4915 statemod.addunfinished(
4916 b'merge',
4916 b'merge',
4917 fname=None,
4917 fname=None,
4918 clearable=True,
4918 clearable=True,
4919 allowcommit=True,
4919 allowcommit=True,
4920 cmdmsg=_(b'outstanding uncommitted merge'),
4920 cmdmsg=_(b'outstanding uncommitted merge'),
4921 abortfunc=hg.abortmerge,
4921 abortfunc=hg.abortmerge,
4922 statushint=_(
4922 statushint=_(
4923 b'To continue: hg commit\nTo abort: hg merge --abort'
4923 b'To continue: hg commit\nTo abort: hg merge --abort'
4924 ),
4924 ),
4925 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4925 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4926 )
4926 )
4927
4927
4928
4928
4929 @command(
4929 @command(
4930 b'outgoing|out',
4930 b'outgoing|out',
4931 [
4931 [
4932 (
4932 (
4933 b'f',
4933 b'f',
4934 b'force',
4934 b'force',
4935 None,
4935 None,
4936 _(b'run even when the destination is unrelated'),
4936 _(b'run even when the destination is unrelated'),
4937 ),
4937 ),
4938 (
4938 (
4939 b'r',
4939 b'r',
4940 b'rev',
4940 b'rev',
4941 [],
4941 [],
4942 _(b'a changeset intended to be included in the destination'),
4942 _(b'a changeset intended to be included in the destination'),
4943 _(b'REV'),
4943 _(b'REV'),
4944 ),
4944 ),
4945 (b'n', b'newest-first', None, _(b'show newest record first')),
4945 (b'n', b'newest-first', None, _(b'show newest record first')),
4946 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4946 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4947 (
4947 (
4948 b'b',
4948 b'b',
4949 b'branch',
4949 b'branch',
4950 [],
4950 [],
4951 _(b'a specific branch you would like to push'),
4951 _(b'a specific branch you would like to push'),
4952 _(b'BRANCH'),
4952 _(b'BRANCH'),
4953 ),
4953 ),
4954 ]
4954 ]
4955 + logopts
4955 + logopts
4956 + remoteopts
4956 + remoteopts
4957 + subrepoopts,
4957 + subrepoopts,
4958 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4958 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4959 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4959 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4960 )
4960 )
4961 def outgoing(ui, repo, dest=None, **opts):
4961 def outgoing(ui, repo, dest=None, **opts):
4962 """show changesets not found in the destination
4962 """show changesets not found in the destination
4963
4963
4964 Show changesets not found in the specified destination repository
4964 Show changesets not found in the specified destination repository
4965 or the default push location. These are the changesets that would
4965 or the default push location. These are the changesets that would
4966 be pushed if a push was requested.
4966 be pushed if a push was requested.
4967
4967
4968 See pull for details of valid destination formats.
4968 See pull for details of valid destination formats.
4969
4969
4970 .. container:: verbose
4970 .. container:: verbose
4971
4971
4972 With -B/--bookmarks, the result of bookmark comparison between
4972 With -B/--bookmarks, the result of bookmark comparison between
4973 local and remote repositories is displayed. With -v/--verbose,
4973 local and remote repositories is displayed. With -v/--verbose,
4974 status is also displayed for each bookmark like below::
4974 status is also displayed for each bookmark like below::
4975
4975
4976 BM1 01234567890a added
4976 BM1 01234567890a added
4977 BM2 deleted
4977 BM2 deleted
4978 BM3 234567890abc advanced
4978 BM3 234567890abc advanced
4979 BM4 34567890abcd diverged
4979 BM4 34567890abcd diverged
4980 BM5 4567890abcde changed
4980 BM5 4567890abcde changed
4981
4981
4982 The action taken when pushing depends on the
4982 The action taken when pushing depends on the
4983 status of each bookmark:
4983 status of each bookmark:
4984
4984
4985 :``added``: push with ``-B`` will create it
4985 :``added``: push with ``-B`` will create it
4986 :``deleted``: push with ``-B`` will delete it
4986 :``deleted``: push with ``-B`` will delete it
4987 :``advanced``: push will update it
4987 :``advanced``: push will update it
4988 :``diverged``: push with ``-B`` will update it
4988 :``diverged``: push with ``-B`` will update it
4989 :``changed``: push with ``-B`` will update it
4989 :``changed``: push with ``-B`` will update it
4990
4990
4991 From the point of view of pushing behavior, bookmarks
4991 From the point of view of pushing behavior, bookmarks
4992 existing only in the remote repository are treated as
4992 existing only in the remote repository are treated as
4993 ``deleted``, even if it is in fact added remotely.
4993 ``deleted``, even if it is in fact added remotely.
4994
4994
4995 Returns 0 if there are outgoing changes, 1 otherwise.
4995 Returns 0 if there are outgoing changes, 1 otherwise.
4996 """
4996 """
4997 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4997 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4998 # style URLs, so don't overwrite dest.
4998 # style URLs, so don't overwrite dest.
4999 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4999 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5000 if not path:
5000 if not path:
5001 raise error.Abort(
5001 raise error.Abort(
5002 _(b'default repository not configured!'),
5002 _(b'default repository not configured!'),
5003 hint=_(b"see 'hg help config.paths'"),
5003 hint=_(b"see 'hg help config.paths'"),
5004 )
5004 )
5005
5005
5006 opts = pycompat.byteskwargs(opts)
5006 opts = pycompat.byteskwargs(opts)
5007 if opts.get(b'graph'):
5007 if opts.get(b'graph'):
5008 logcmdutil.checkunsupportedgraphflags([], opts)
5008 logcmdutil.checkunsupportedgraphflags([], opts)
5009 o, other = hg._outgoing(ui, repo, dest, opts)
5009 o, other = hg._outgoing(ui, repo, dest, opts)
5010 if not o:
5010 if not o:
5011 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5011 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5012 return
5012 return
5013
5013
5014 revdag = logcmdutil.graphrevs(repo, o, opts)
5014 revdag = logcmdutil.graphrevs(repo, o, opts)
5015 ui.pager(b'outgoing')
5015 ui.pager(b'outgoing')
5016 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5016 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5017 logcmdutil.displaygraph(
5017 logcmdutil.displaygraph(
5018 ui, repo, revdag, displayer, graphmod.asciiedges
5018 ui, repo, revdag, displayer, graphmod.asciiedges
5019 )
5019 )
5020 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5020 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5021 return 0
5021 return 0
5022
5022
5023 if opts.get(b'bookmarks'):
5023 if opts.get(b'bookmarks'):
5024 dest = path.pushloc or path.loc
5024 dest = path.pushloc or path.loc
5025 other = hg.peer(repo, opts, dest)
5025 other = hg.peer(repo, opts, dest)
5026 if b'bookmarks' not in other.listkeys(b'namespaces'):
5026 if b'bookmarks' not in other.listkeys(b'namespaces'):
5027 ui.warn(_(b"remote doesn't support bookmarks\n"))
5027 ui.warn(_(b"remote doesn't support bookmarks\n"))
5028 return 0
5028 return 0
5029 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5029 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5030 ui.pager(b'outgoing')
5030 ui.pager(b'outgoing')
5031 return bookmarks.outgoing(ui, repo, other)
5031 return bookmarks.outgoing(ui, repo, other)
5032
5032
5033 repo._subtoppath = path.pushloc or path.loc
5033 repo._subtoppath = path.pushloc or path.loc
5034 try:
5034 try:
5035 return hg.outgoing(ui, repo, dest, opts)
5035 return hg.outgoing(ui, repo, dest, opts)
5036 finally:
5036 finally:
5037 del repo._subtoppath
5037 del repo._subtoppath
5038
5038
5039
5039
5040 @command(
5040 @command(
5041 b'parents',
5041 b'parents',
5042 [
5042 [
5043 (
5043 (
5044 b'r',
5044 b'r',
5045 b'rev',
5045 b'rev',
5046 b'',
5046 b'',
5047 _(b'show parents of the specified revision'),
5047 _(b'show parents of the specified revision'),
5048 _(b'REV'),
5048 _(b'REV'),
5049 ),
5049 ),
5050 ]
5050 ]
5051 + templateopts,
5051 + templateopts,
5052 _(b'[-r REV] [FILE]'),
5052 _(b'[-r REV] [FILE]'),
5053 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5053 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5054 inferrepo=True,
5054 inferrepo=True,
5055 )
5055 )
5056 def parents(ui, repo, file_=None, **opts):
5056 def parents(ui, repo, file_=None, **opts):
5057 """show the parents of the working directory or revision (DEPRECATED)
5057 """show the parents of the working directory or revision (DEPRECATED)
5058
5058
5059 Print the working directory's parent revisions. If a revision is
5059 Print the working directory's parent revisions. If a revision is
5060 given via -r/--rev, the parent of that revision will be printed.
5060 given via -r/--rev, the parent of that revision will be printed.
5061 If a file argument is given, the revision in which the file was
5061 If a file argument is given, the revision in which the file was
5062 last changed (before the working directory revision or the
5062 last changed (before the working directory revision or the
5063 argument to --rev if given) is printed.
5063 argument to --rev if given) is printed.
5064
5064
5065 This command is equivalent to::
5065 This command is equivalent to::
5066
5066
5067 hg log -r "p1()+p2()" or
5067 hg log -r "p1()+p2()" or
5068 hg log -r "p1(REV)+p2(REV)" or
5068 hg log -r "p1(REV)+p2(REV)" or
5069 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5069 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5070 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5070 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5071
5071
5072 See :hg:`summary` and :hg:`help revsets` for related information.
5072 See :hg:`summary` and :hg:`help revsets` for related information.
5073
5073
5074 Returns 0 on success.
5074 Returns 0 on success.
5075 """
5075 """
5076
5076
5077 opts = pycompat.byteskwargs(opts)
5077 opts = pycompat.byteskwargs(opts)
5078 rev = opts.get(b'rev')
5078 rev = opts.get(b'rev')
5079 if rev:
5079 if rev:
5080 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5080 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5081 ctx = scmutil.revsingle(repo, rev, None)
5081 ctx = scmutil.revsingle(repo, rev, None)
5082
5082
5083 if file_:
5083 if file_:
5084 m = scmutil.match(ctx, (file_,), opts)
5084 m = scmutil.match(ctx, (file_,), opts)
5085 if m.anypats() or len(m.files()) != 1:
5085 if m.anypats() or len(m.files()) != 1:
5086 raise error.Abort(_(b'can only specify an explicit filename'))
5086 raise error.Abort(_(b'can only specify an explicit filename'))
5087 file_ = m.files()[0]
5087 file_ = m.files()[0]
5088 filenodes = []
5088 filenodes = []
5089 for cp in ctx.parents():
5089 for cp in ctx.parents():
5090 if not cp:
5090 if not cp:
5091 continue
5091 continue
5092 try:
5092 try:
5093 filenodes.append(cp.filenode(file_))
5093 filenodes.append(cp.filenode(file_))
5094 except error.LookupError:
5094 except error.LookupError:
5095 pass
5095 pass
5096 if not filenodes:
5096 if not filenodes:
5097 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5097 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5098 p = []
5098 p = []
5099 for fn in filenodes:
5099 for fn in filenodes:
5100 fctx = repo.filectx(file_, fileid=fn)
5100 fctx = repo.filectx(file_, fileid=fn)
5101 p.append(fctx.node())
5101 p.append(fctx.node())
5102 else:
5102 else:
5103 p = [cp.node() for cp in ctx.parents()]
5103 p = [cp.node() for cp in ctx.parents()]
5104
5104
5105 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5105 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5106 for n in p:
5106 for n in p:
5107 if n != nullid:
5107 if n != nullid:
5108 displayer.show(repo[n])
5108 displayer.show(repo[n])
5109 displayer.close()
5109 displayer.close()
5110
5110
5111
5111
5112 @command(
5112 @command(
5113 b'paths',
5113 b'paths',
5114 formatteropts,
5114 formatteropts,
5115 _(b'[NAME]'),
5115 _(b'[NAME]'),
5116 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5116 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5117 optionalrepo=True,
5117 optionalrepo=True,
5118 intents={INTENT_READONLY},
5118 intents={INTENT_READONLY},
5119 )
5119 )
5120 def paths(ui, repo, search=None, **opts):
5120 def paths(ui, repo, search=None, **opts):
5121 """show aliases for remote repositories
5121 """show aliases for remote repositories
5122
5122
5123 Show definition of symbolic path name NAME. If no name is given,
5123 Show definition of symbolic path name NAME. If no name is given,
5124 show definition of all available names.
5124 show definition of all available names.
5125
5125
5126 Option -q/--quiet suppresses all output when searching for NAME
5126 Option -q/--quiet suppresses all output when searching for NAME
5127 and shows only the path names when listing all definitions.
5127 and shows only the path names when listing all definitions.
5128
5128
5129 Path names are defined in the [paths] section of your
5129 Path names are defined in the [paths] section of your
5130 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5130 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5131 repository, ``.hg/hgrc`` is used, too.
5131 repository, ``.hg/hgrc`` is used, too.
5132
5132
5133 The path names ``default`` and ``default-push`` have a special
5133 The path names ``default`` and ``default-push`` have a special
5134 meaning. When performing a push or pull operation, they are used
5134 meaning. When performing a push or pull operation, they are used
5135 as fallbacks if no location is specified on the command-line.
5135 as fallbacks if no location is specified on the command-line.
5136 When ``default-push`` is set, it will be used for push and
5136 When ``default-push`` is set, it will be used for push and
5137 ``default`` will be used for pull; otherwise ``default`` is used
5137 ``default`` will be used for pull; otherwise ``default`` is used
5138 as the fallback for both. When cloning a repository, the clone
5138 as the fallback for both. When cloning a repository, the clone
5139 source is written as ``default`` in ``.hg/hgrc``.
5139 source is written as ``default`` in ``.hg/hgrc``.
5140
5140
5141 .. note::
5141 .. note::
5142
5142
5143 ``default`` and ``default-push`` apply to all inbound (e.g.
5143 ``default`` and ``default-push`` apply to all inbound (e.g.
5144 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5144 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5145 and :hg:`bundle`) operations.
5145 and :hg:`bundle`) operations.
5146
5146
5147 See :hg:`help urls` for more information.
5147 See :hg:`help urls` for more information.
5148
5148
5149 .. container:: verbose
5149 .. container:: verbose
5150
5150
5151 Template:
5151 Template:
5152
5152
5153 The following keywords are supported. See also :hg:`help templates`.
5153 The following keywords are supported. See also :hg:`help templates`.
5154
5154
5155 :name: String. Symbolic name of the path alias.
5155 :name: String. Symbolic name of the path alias.
5156 :pushurl: String. URL for push operations.
5156 :pushurl: String. URL for push operations.
5157 :url: String. URL or directory path for the other operations.
5157 :url: String. URL or directory path for the other operations.
5158
5158
5159 Returns 0 on success.
5159 Returns 0 on success.
5160 """
5160 """
5161
5161
5162 opts = pycompat.byteskwargs(opts)
5162 opts = pycompat.byteskwargs(opts)
5163 ui.pager(b'paths')
5163 ui.pager(b'paths')
5164 if search:
5164 if search:
5165 pathitems = [
5165 pathitems = [
5166 (name, path)
5166 (name, path)
5167 for name, path in pycompat.iteritems(ui.paths)
5167 for name, path in pycompat.iteritems(ui.paths)
5168 if name == search
5168 if name == search
5169 ]
5169 ]
5170 else:
5170 else:
5171 pathitems = sorted(pycompat.iteritems(ui.paths))
5171 pathitems = sorted(pycompat.iteritems(ui.paths))
5172
5172
5173 fm = ui.formatter(b'paths', opts)
5173 fm = ui.formatter(b'paths', opts)
5174 if fm.isplain():
5174 if fm.isplain():
5175 hidepassword = util.hidepassword
5175 hidepassword = util.hidepassword
5176 else:
5176 else:
5177 hidepassword = bytes
5177 hidepassword = bytes
5178 if ui.quiet:
5178 if ui.quiet:
5179 namefmt = b'%s\n'
5179 namefmt = b'%s\n'
5180 else:
5180 else:
5181 namefmt = b'%s = '
5181 namefmt = b'%s = '
5182 showsubopts = not search and not ui.quiet
5182 showsubopts = not search and not ui.quiet
5183
5183
5184 for name, path in pathitems:
5184 for name, path in pathitems:
5185 fm.startitem()
5185 fm.startitem()
5186 fm.condwrite(not search, b'name', namefmt, name)
5186 fm.condwrite(not search, b'name', namefmt, name)
5187 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5187 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5188 for subopt, value in sorted(path.suboptions.items()):
5188 for subopt, value in sorted(path.suboptions.items()):
5189 assert subopt not in (b'name', b'url')
5189 assert subopt not in (b'name', b'url')
5190 if showsubopts:
5190 if showsubopts:
5191 fm.plain(b'%s:%s = ' % (name, subopt))
5191 fm.plain(b'%s:%s = ' % (name, subopt))
5192 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5192 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5193
5193
5194 fm.end()
5194 fm.end()
5195
5195
5196 if search and not pathitems:
5196 if search and not pathitems:
5197 if not ui.quiet:
5197 if not ui.quiet:
5198 ui.warn(_(b"not found!\n"))
5198 ui.warn(_(b"not found!\n"))
5199 return 1
5199 return 1
5200 else:
5200 else:
5201 return 0
5201 return 0
5202
5202
5203
5203
5204 @command(
5204 @command(
5205 b'phase',
5205 b'phase',
5206 [
5206 [
5207 (b'p', b'public', False, _(b'set changeset phase to public')),
5207 (b'p', b'public', False, _(b'set changeset phase to public')),
5208 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5208 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5209 (b's', b'secret', False, _(b'set changeset phase to secret')),
5209 (b's', b'secret', False, _(b'set changeset phase to secret')),
5210 (b'f', b'force', False, _(b'allow to move boundary backward')),
5210 (b'f', b'force', False, _(b'allow to move boundary backward')),
5211 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5211 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5212 ],
5212 ],
5213 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5213 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5214 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5214 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5215 )
5215 )
5216 def phase(ui, repo, *revs, **opts):
5216 def phase(ui, repo, *revs, **opts):
5217 """set or show the current phase name
5217 """set or show the current phase name
5218
5218
5219 With no argument, show the phase name of the current revision(s).
5219 With no argument, show the phase name of the current revision(s).
5220
5220
5221 With one of -p/--public, -d/--draft or -s/--secret, change the
5221 With one of -p/--public, -d/--draft or -s/--secret, change the
5222 phase value of the specified revisions.
5222 phase value of the specified revisions.
5223
5223
5224 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5224 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5225 lower phase to a higher phase. Phases are ordered as follows::
5225 lower phase to a higher phase. Phases are ordered as follows::
5226
5226
5227 public < draft < secret
5227 public < draft < secret
5228
5228
5229 Returns 0 on success, 1 if some phases could not be changed.
5229 Returns 0 on success, 1 if some phases could not be changed.
5230
5230
5231 (For more information about the phases concept, see :hg:`help phases`.)
5231 (For more information about the phases concept, see :hg:`help phases`.)
5232 """
5232 """
5233 opts = pycompat.byteskwargs(opts)
5233 opts = pycompat.byteskwargs(opts)
5234 # search for a unique phase argument
5234 # search for a unique phase argument
5235 targetphase = None
5235 targetphase = None
5236 for idx, name in enumerate(phases.cmdphasenames):
5236 for idx, name in enumerate(phases.cmdphasenames):
5237 if opts[name]:
5237 if opts[name]:
5238 if targetphase is not None:
5238 if targetphase is not None:
5239 raise error.Abort(_(b'only one phase can be specified'))
5239 raise error.Abort(_(b'only one phase can be specified'))
5240 targetphase = idx
5240 targetphase = idx
5241
5241
5242 # look for specified revision
5242 # look for specified revision
5243 revs = list(revs)
5243 revs = list(revs)
5244 revs.extend(opts[b'rev'])
5244 revs.extend(opts[b'rev'])
5245 if not revs:
5245 if not revs:
5246 # display both parents as the second parent phase can influence
5246 # display both parents as the second parent phase can influence
5247 # the phase of a merge commit
5247 # the phase of a merge commit
5248 revs = [c.rev() for c in repo[None].parents()]
5248 revs = [c.rev() for c in repo[None].parents()]
5249
5249
5250 revs = scmutil.revrange(repo, revs)
5250 revs = scmutil.revrange(repo, revs)
5251
5251
5252 ret = 0
5252 ret = 0
5253 if targetphase is None:
5253 if targetphase is None:
5254 # display
5254 # display
5255 for r in revs:
5255 for r in revs:
5256 ctx = repo[r]
5256 ctx = repo[r]
5257 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5257 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5258 else:
5258 else:
5259 with repo.lock(), repo.transaction(b"phase") as tr:
5259 with repo.lock(), repo.transaction(b"phase") as tr:
5260 # set phase
5260 # set phase
5261 if not revs:
5261 if not revs:
5262 raise error.Abort(_(b'empty revision set'))
5262 raise error.Abort(_(b'empty revision set'))
5263 nodes = [repo[r].node() for r in revs]
5263 nodes = [repo[r].node() for r in revs]
5264 # moving revision from public to draft may hide them
5264 # moving revision from public to draft may hide them
5265 # We have to check result on an unfiltered repository
5265 # We have to check result on an unfiltered repository
5266 unfi = repo.unfiltered()
5266 unfi = repo.unfiltered()
5267 getphase = unfi._phasecache.phase
5267 getphase = unfi._phasecache.phase
5268 olddata = [getphase(unfi, r) for r in unfi]
5268 olddata = [getphase(unfi, r) for r in unfi]
5269 phases.advanceboundary(repo, tr, targetphase, nodes)
5269 phases.advanceboundary(repo, tr, targetphase, nodes)
5270 if opts[b'force']:
5270 if opts[b'force']:
5271 phases.retractboundary(repo, tr, targetphase, nodes)
5271 phases.retractboundary(repo, tr, targetphase, nodes)
5272 getphase = unfi._phasecache.phase
5272 getphase = unfi._phasecache.phase
5273 newdata = [getphase(unfi, r) for r in unfi]
5273 newdata = [getphase(unfi, r) for r in unfi]
5274 changes = sum(newdata[r] != olddata[r] for r in unfi)
5274 changes = sum(newdata[r] != olddata[r] for r in unfi)
5275 cl = unfi.changelog
5275 cl = unfi.changelog
5276 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5276 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5277 if rejected:
5277 if rejected:
5278 ui.warn(
5278 ui.warn(
5279 _(
5279 _(
5280 b'cannot move %i changesets to a higher '
5280 b'cannot move %i changesets to a higher '
5281 b'phase, use --force\n'
5281 b'phase, use --force\n'
5282 )
5282 )
5283 % len(rejected)
5283 % len(rejected)
5284 )
5284 )
5285 ret = 1
5285 ret = 1
5286 if changes:
5286 if changes:
5287 msg = _(b'phase changed for %i changesets\n') % changes
5287 msg = _(b'phase changed for %i changesets\n') % changes
5288 if ret:
5288 if ret:
5289 ui.status(msg)
5289 ui.status(msg)
5290 else:
5290 else:
5291 ui.note(msg)
5291 ui.note(msg)
5292 else:
5292 else:
5293 ui.warn(_(b'no phases changed\n'))
5293 ui.warn(_(b'no phases changed\n'))
5294 return ret
5294 return ret
5295
5295
5296
5296
5297 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5297 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5298 """Run after a changegroup has been added via pull/unbundle
5298 """Run after a changegroup has been added via pull/unbundle
5299
5299
5300 This takes arguments below:
5300 This takes arguments below:
5301
5301
5302 :modheads: change of heads by pull/unbundle
5302 :modheads: change of heads by pull/unbundle
5303 :optupdate: updating working directory is needed or not
5303 :optupdate: updating working directory is needed or not
5304 :checkout: update destination revision (or None to default destination)
5304 :checkout: update destination revision (or None to default destination)
5305 :brev: a name, which might be a bookmark to be activated after updating
5305 :brev: a name, which might be a bookmark to be activated after updating
5306 """
5306 """
5307 if modheads == 0:
5307 if modheads == 0:
5308 return
5308 return
5309 if optupdate:
5309 if optupdate:
5310 try:
5310 try:
5311 return hg.updatetotally(ui, repo, checkout, brev)
5311 return hg.updatetotally(ui, repo, checkout, brev)
5312 except error.UpdateAbort as inst:
5312 except error.UpdateAbort as inst:
5313 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5313 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5314 hint = inst.hint
5314 hint = inst.hint
5315 raise error.UpdateAbort(msg, hint=hint)
5315 raise error.UpdateAbort(msg, hint=hint)
5316 if modheads is not None and modheads > 1:
5316 if modheads is not None and modheads > 1:
5317 currentbranchheads = len(repo.branchheads())
5317 currentbranchheads = len(repo.branchheads())
5318 if currentbranchheads == modheads:
5318 if currentbranchheads == modheads:
5319 ui.status(
5319 ui.status(
5320 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5320 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5321 )
5321 )
5322 elif currentbranchheads > 1:
5322 elif currentbranchheads > 1:
5323 ui.status(
5323 ui.status(
5324 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5324 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5325 )
5325 )
5326 else:
5326 else:
5327 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5327 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5328 elif not ui.configbool(b'commands', b'update.requiredest'):
5328 elif not ui.configbool(b'commands', b'update.requiredest'):
5329 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5329 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5330
5330
5331
5331
5332 @command(
5332 @command(
5333 b'pull',
5333 b'pull',
5334 [
5334 [
5335 (
5335 (
5336 b'u',
5336 b'u',
5337 b'update',
5337 b'update',
5338 None,
5338 None,
5339 _(b'update to new branch head if new descendants were pulled'),
5339 _(b'update to new branch head if new descendants were pulled'),
5340 ),
5340 ),
5341 (
5341 (
5342 b'f',
5342 b'f',
5343 b'force',
5343 b'force',
5344 None,
5344 None,
5345 _(b'run even when remote repository is unrelated'),
5345 _(b'run even when remote repository is unrelated'),
5346 ),
5346 ),
5347 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5347 (
5348 (
5348 b'r',
5349 b'r',
5349 b'rev',
5350 b'rev',
5350 [],
5351 [],
5351 _(b'a remote changeset intended to be added'),
5352 _(b'a remote changeset intended to be added'),
5352 _(b'REV'),
5353 _(b'REV'),
5353 ),
5354 ),
5354 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5355 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5355 (
5356 (
5356 b'b',
5357 b'b',
5357 b'branch',
5358 b'branch',
5358 [],
5359 [],
5359 _(b'a specific branch you would like to pull'),
5360 _(b'a specific branch you would like to pull'),
5360 _(b'BRANCH'),
5361 _(b'BRANCH'),
5361 ),
5362 ),
5362 ]
5363 ]
5363 + remoteopts,
5364 + remoteopts,
5364 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5365 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5365 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5366 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5366 helpbasic=True,
5367 helpbasic=True,
5367 )
5368 )
5368 def pull(ui, repo, source=b"default", **opts):
5369 def pull(ui, repo, source=b"default", **opts):
5369 """pull changes from the specified source
5370 """pull changes from the specified source
5370
5371
5371 Pull changes from a remote repository to a local one.
5372 Pull changes from a remote repository to a local one.
5372
5373
5373 This finds all changes from the repository at the specified path
5374 This finds all changes from the repository at the specified path
5374 or URL and adds them to a local repository (the current one unless
5375 or URL and adds them to a local repository (the current one unless
5375 -R is specified). By default, this does not update the copy of the
5376 -R is specified). By default, this does not update the copy of the
5376 project in the working directory.
5377 project in the working directory.
5377
5378
5378 When cloning from servers that support it, Mercurial may fetch
5379 When cloning from servers that support it, Mercurial may fetch
5379 pre-generated data. When this is done, hooks operating on incoming
5380 pre-generated data. When this is done, hooks operating on incoming
5380 changesets and changegroups may fire more than once, once for each
5381 changesets and changegroups may fire more than once, once for each
5381 pre-generated bundle and as well as for any additional remaining
5382 pre-generated bundle and as well as for any additional remaining
5382 data. See :hg:`help -e clonebundles` for more.
5383 data. See :hg:`help -e clonebundles` for more.
5383
5384
5384 Use :hg:`incoming` if you want to see what would have been added
5385 Use :hg:`incoming` if you want to see what would have been added
5385 by a pull at the time you issued this command. If you then decide
5386 by a pull at the time you issued this command. If you then decide
5386 to add those changes to the repository, you should use :hg:`pull
5387 to add those changes to the repository, you should use :hg:`pull
5387 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5388 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5388
5389
5389 If SOURCE is omitted, the 'default' path will be used.
5390 If SOURCE is omitted, the 'default' path will be used.
5390 See :hg:`help urls` for more information.
5391 See :hg:`help urls` for more information.
5391
5392
5392 Specifying bookmark as ``.`` is equivalent to specifying the active
5393 Specifying bookmark as ``.`` is equivalent to specifying the active
5393 bookmark's name.
5394 bookmark's name.
5394
5395
5395 Returns 0 on success, 1 if an update had unresolved files.
5396 Returns 0 on success, 1 if an update had unresolved files.
5396 """
5397 """
5397
5398
5398 opts = pycompat.byteskwargs(opts)
5399 opts = pycompat.byteskwargs(opts)
5399 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5400 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5400 b'update'
5401 b'update'
5401 ):
5402 ):
5402 msg = _(b'update destination required by configuration')
5403 msg = _(b'update destination required by configuration')
5403 hint = _(b'use hg pull followed by hg update DEST')
5404 hint = _(b'use hg pull followed by hg update DEST')
5404 raise error.Abort(msg, hint=hint)
5405 raise error.Abort(msg, hint=hint)
5405
5406
5406 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5407 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5407 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5408 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5408 other = hg.peer(repo, opts, source)
5409 other = hg.peer(repo, opts, source)
5409 try:
5410 try:
5410 revs, checkout = hg.addbranchrevs(
5411 revs, checkout = hg.addbranchrevs(
5411 repo, other, branches, opts.get(b'rev')
5412 repo, other, branches, opts.get(b'rev')
5412 )
5413 )
5413
5414
5414 pullopargs = {}
5415 pullopargs = {}
5415
5416
5416 nodes = None
5417 nodes = None
5417 if opts.get(b'bookmark') or revs:
5418 if opts.get(b'bookmark') or revs:
5418 # The list of bookmark used here is the same used to actually update
5419 # The list of bookmark used here is the same used to actually update
5419 # the bookmark names, to avoid the race from issue 4689 and we do
5420 # the bookmark names, to avoid the race from issue 4689 and we do
5420 # all lookup and bookmark queries in one go so they see the same
5421 # all lookup and bookmark queries in one go so they see the same
5421 # version of the server state (issue 4700).
5422 # version of the server state (issue 4700).
5422 nodes = []
5423 nodes = []
5423 fnodes = []
5424 fnodes = []
5424 revs = revs or []
5425 revs = revs or []
5425 if revs and not other.capable(b'lookup'):
5426 if revs and not other.capable(b'lookup'):
5426 err = _(
5427 err = _(
5427 b"other repository doesn't support revision lookup, "
5428 b"other repository doesn't support revision lookup, "
5428 b"so a rev cannot be specified."
5429 b"so a rev cannot be specified."
5429 )
5430 )
5430 raise error.Abort(err)
5431 raise error.Abort(err)
5431 with other.commandexecutor() as e:
5432 with other.commandexecutor() as e:
5432 fremotebookmarks = e.callcommand(
5433 fremotebookmarks = e.callcommand(
5433 b'listkeys', {b'namespace': b'bookmarks'}
5434 b'listkeys', {b'namespace': b'bookmarks'}
5434 )
5435 )
5435 for r in revs:
5436 for r in revs:
5436 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5437 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5437 remotebookmarks = fremotebookmarks.result()
5438 remotebookmarks = fremotebookmarks.result()
5438 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5439 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5439 pullopargs[b'remotebookmarks'] = remotebookmarks
5440 pullopargs[b'remotebookmarks'] = remotebookmarks
5440 for b in opts.get(b'bookmark', []):
5441 for b in opts.get(b'bookmark', []):
5441 b = repo._bookmarks.expandname(b)
5442 b = repo._bookmarks.expandname(b)
5442 if b not in remotebookmarks:
5443 if b not in remotebookmarks:
5443 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5444 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5444 nodes.append(remotebookmarks[b])
5445 nodes.append(remotebookmarks[b])
5445 for i, rev in enumerate(revs):
5446 for i, rev in enumerate(revs):
5446 node = fnodes[i].result()
5447 node = fnodes[i].result()
5447 nodes.append(node)
5448 nodes.append(node)
5448 if rev == checkout:
5449 if rev == checkout:
5449 checkout = node
5450 checkout = node
5450
5451
5451 wlock = util.nullcontextmanager()
5452 wlock = util.nullcontextmanager()
5452 if opts.get(b'update'):
5453 if opts.get(b'update'):
5453 wlock = repo.wlock()
5454 wlock = repo.wlock()
5454 with wlock:
5455 with wlock:
5455 pullopargs.update(opts.get(b'opargs', {}))
5456 pullopargs.update(opts.get(b'opargs', {}))
5456 modheads = exchange.pull(
5457 modheads = exchange.pull(
5457 repo,
5458 repo,
5458 other,
5459 other,
5459 heads=nodes,
5460 heads=nodes,
5460 force=opts.get(b'force'),
5461 force=opts.get(b'force'),
5461 bookmarks=opts.get(b'bookmark', ()),
5462 bookmarks=opts.get(b'bookmark', ()),
5462 opargs=pullopargs,
5463 opargs=pullopargs,
5464 confirm=opts.get(b'confirm'),
5463 ).cgresult
5465 ).cgresult
5464
5466
5465 # brev is a name, which might be a bookmark to be activated at
5467 # brev is a name, which might be a bookmark to be activated at
5466 # the end of the update. In other words, it is an explicit
5468 # the end of the update. In other words, it is an explicit
5467 # destination of the update
5469 # destination of the update
5468 brev = None
5470 brev = None
5469
5471
5470 if checkout:
5472 if checkout:
5471 checkout = repo.unfiltered().changelog.rev(checkout)
5473 checkout = repo.unfiltered().changelog.rev(checkout)
5472
5474
5473 # order below depends on implementation of
5475 # order below depends on implementation of
5474 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5476 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5475 # because 'checkout' is determined without it.
5477 # because 'checkout' is determined without it.
5476 if opts.get(b'rev'):
5478 if opts.get(b'rev'):
5477 brev = opts[b'rev'][0]
5479 brev = opts[b'rev'][0]
5478 elif opts.get(b'branch'):
5480 elif opts.get(b'branch'):
5479 brev = opts[b'branch'][0]
5481 brev = opts[b'branch'][0]
5480 else:
5482 else:
5481 brev = branches[0]
5483 brev = branches[0]
5482 repo._subtoppath = source
5484 repo._subtoppath = source
5483 try:
5485 try:
5484 ret = postincoming(
5486 ret = postincoming(
5485 ui, repo, modheads, opts.get(b'update'), checkout, brev
5487 ui, repo, modheads, opts.get(b'update'), checkout, brev
5486 )
5488 )
5487 except error.FilteredRepoLookupError as exc:
5489 except error.FilteredRepoLookupError as exc:
5488 msg = _(b'cannot update to target: %s') % exc.args[0]
5490 msg = _(b'cannot update to target: %s') % exc.args[0]
5489 exc.args = (msg,) + exc.args[1:]
5491 exc.args = (msg,) + exc.args[1:]
5490 raise
5492 raise
5491 finally:
5493 finally:
5492 del repo._subtoppath
5494 del repo._subtoppath
5493
5495
5494 finally:
5496 finally:
5495 other.close()
5497 other.close()
5496 return ret
5498 return ret
5497
5499
5498
5500
5499 @command(
5501 @command(
5500 b'push',
5502 b'push',
5501 [
5503 [
5502 (b'f', b'force', None, _(b'force push')),
5504 (b'f', b'force', None, _(b'force push')),
5503 (
5505 (
5504 b'r',
5506 b'r',
5505 b'rev',
5507 b'rev',
5506 [],
5508 [],
5507 _(b'a changeset intended to be included in the destination'),
5509 _(b'a changeset intended to be included in the destination'),
5508 _(b'REV'),
5510 _(b'REV'),
5509 ),
5511 ),
5510 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5512 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5511 (
5513 (
5512 b'b',
5514 b'b',
5513 b'branch',
5515 b'branch',
5514 [],
5516 [],
5515 _(b'a specific branch you would like to push'),
5517 _(b'a specific branch you would like to push'),
5516 _(b'BRANCH'),
5518 _(b'BRANCH'),
5517 ),
5519 ),
5518 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5520 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5519 (
5521 (
5520 b'',
5522 b'',
5521 b'pushvars',
5523 b'pushvars',
5522 [],
5524 [],
5523 _(b'variables that can be sent to server (ADVANCED)'),
5525 _(b'variables that can be sent to server (ADVANCED)'),
5524 ),
5526 ),
5525 (
5527 (
5526 b'',
5528 b'',
5527 b'publish',
5529 b'publish',
5528 False,
5530 False,
5529 _(b'push the changeset as public (EXPERIMENTAL)'),
5531 _(b'push the changeset as public (EXPERIMENTAL)'),
5530 ),
5532 ),
5531 ]
5533 ]
5532 + remoteopts,
5534 + remoteopts,
5533 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5535 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5534 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5536 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5535 helpbasic=True,
5537 helpbasic=True,
5536 )
5538 )
5537 def push(ui, repo, dest=None, **opts):
5539 def push(ui, repo, dest=None, **opts):
5538 """push changes to the specified destination
5540 """push changes to the specified destination
5539
5541
5540 Push changesets from the local repository to the specified
5542 Push changesets from the local repository to the specified
5541 destination.
5543 destination.
5542
5544
5543 This operation is symmetrical to pull: it is identical to a pull
5545 This operation is symmetrical to pull: it is identical to a pull
5544 in the destination repository from the current one.
5546 in the destination repository from the current one.
5545
5547
5546 By default, push will not allow creation of new heads at the
5548 By default, push will not allow creation of new heads at the
5547 destination, since multiple heads would make it unclear which head
5549 destination, since multiple heads would make it unclear which head
5548 to use. In this situation, it is recommended to pull and merge
5550 to use. In this situation, it is recommended to pull and merge
5549 before pushing.
5551 before pushing.
5550
5552
5551 Use --new-branch if you want to allow push to create a new named
5553 Use --new-branch if you want to allow push to create a new named
5552 branch that is not present at the destination. This allows you to
5554 branch that is not present at the destination. This allows you to
5553 only create a new branch without forcing other changes.
5555 only create a new branch without forcing other changes.
5554
5556
5555 .. note::
5557 .. note::
5556
5558
5557 Extra care should be taken with the -f/--force option,
5559 Extra care should be taken with the -f/--force option,
5558 which will push all new heads on all branches, an action which will
5560 which will push all new heads on all branches, an action which will
5559 almost always cause confusion for collaborators.
5561 almost always cause confusion for collaborators.
5560
5562
5561 If -r/--rev is used, the specified revision and all its ancestors
5563 If -r/--rev is used, the specified revision and all its ancestors
5562 will be pushed to the remote repository.
5564 will be pushed to the remote repository.
5563
5565
5564 If -B/--bookmark is used, the specified bookmarked revision, its
5566 If -B/--bookmark is used, the specified bookmarked revision, its
5565 ancestors, and the bookmark will be pushed to the remote
5567 ancestors, and the bookmark will be pushed to the remote
5566 repository. Specifying ``.`` is equivalent to specifying the active
5568 repository. Specifying ``.`` is equivalent to specifying the active
5567 bookmark's name.
5569 bookmark's name.
5568
5570
5569 Please see :hg:`help urls` for important details about ``ssh://``
5571 Please see :hg:`help urls` for important details about ``ssh://``
5570 URLs. If DESTINATION is omitted, a default path will be used.
5572 URLs. If DESTINATION is omitted, a default path will be used.
5571
5573
5572 .. container:: verbose
5574 .. container:: verbose
5573
5575
5574 The --pushvars option sends strings to the server that become
5576 The --pushvars option sends strings to the server that become
5575 environment variables prepended with ``HG_USERVAR_``. For example,
5577 environment variables prepended with ``HG_USERVAR_``. For example,
5576 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5578 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5577 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5579 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5578
5580
5579 pushvars can provide for user-overridable hooks as well as set debug
5581 pushvars can provide for user-overridable hooks as well as set debug
5580 levels. One example is having a hook that blocks commits containing
5582 levels. One example is having a hook that blocks commits containing
5581 conflict markers, but enables the user to override the hook if the file
5583 conflict markers, but enables the user to override the hook if the file
5582 is using conflict markers for testing purposes or the file format has
5584 is using conflict markers for testing purposes or the file format has
5583 strings that look like conflict markers.
5585 strings that look like conflict markers.
5584
5586
5585 By default, servers will ignore `--pushvars`. To enable it add the
5587 By default, servers will ignore `--pushvars`. To enable it add the
5586 following to your configuration file::
5588 following to your configuration file::
5587
5589
5588 [push]
5590 [push]
5589 pushvars.server = true
5591 pushvars.server = true
5590
5592
5591 Returns 0 if push was successful, 1 if nothing to push.
5593 Returns 0 if push was successful, 1 if nothing to push.
5592 """
5594 """
5593
5595
5594 opts = pycompat.byteskwargs(opts)
5596 opts = pycompat.byteskwargs(opts)
5595 if opts.get(b'bookmark'):
5597 if opts.get(b'bookmark'):
5596 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5598 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5597 for b in opts[b'bookmark']:
5599 for b in opts[b'bookmark']:
5598 # translate -B options to -r so changesets get pushed
5600 # translate -B options to -r so changesets get pushed
5599 b = repo._bookmarks.expandname(b)
5601 b = repo._bookmarks.expandname(b)
5600 if b in repo._bookmarks:
5602 if b in repo._bookmarks:
5601 opts.setdefault(b'rev', []).append(b)
5603 opts.setdefault(b'rev', []).append(b)
5602 else:
5604 else:
5603 # if we try to push a deleted bookmark, translate it to null
5605 # if we try to push a deleted bookmark, translate it to null
5604 # this lets simultaneous -r, -b options continue working
5606 # this lets simultaneous -r, -b options continue working
5605 opts.setdefault(b'rev', []).append(b"null")
5607 opts.setdefault(b'rev', []).append(b"null")
5606
5608
5607 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5609 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5608 if not path:
5610 if not path:
5609 raise error.Abort(
5611 raise error.Abort(
5610 _(b'default repository not configured!'),
5612 _(b'default repository not configured!'),
5611 hint=_(b"see 'hg help config.paths'"),
5613 hint=_(b"see 'hg help config.paths'"),
5612 )
5614 )
5613 dest = path.pushloc or path.loc
5615 dest = path.pushloc or path.loc
5614 branches = (path.branch, opts.get(b'branch') or [])
5616 branches = (path.branch, opts.get(b'branch') or [])
5615 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5617 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5616 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5618 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5617 other = hg.peer(repo, opts, dest)
5619 other = hg.peer(repo, opts, dest)
5618
5620
5619 if revs:
5621 if revs:
5620 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5622 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5621 if not revs:
5623 if not revs:
5622 raise error.Abort(
5624 raise error.Abort(
5623 _(b"specified revisions evaluate to an empty set"),
5625 _(b"specified revisions evaluate to an empty set"),
5624 hint=_(b"use different revision arguments"),
5626 hint=_(b"use different revision arguments"),
5625 )
5627 )
5626 elif path.pushrev:
5628 elif path.pushrev:
5627 # It doesn't make any sense to specify ancestor revisions. So limit
5629 # It doesn't make any sense to specify ancestor revisions. So limit
5628 # to DAG heads to make discovery simpler.
5630 # to DAG heads to make discovery simpler.
5629 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5631 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5630 revs = scmutil.revrange(repo, [expr])
5632 revs = scmutil.revrange(repo, [expr])
5631 revs = [repo[rev].node() for rev in revs]
5633 revs = [repo[rev].node() for rev in revs]
5632 if not revs:
5634 if not revs:
5633 raise error.Abort(
5635 raise error.Abort(
5634 _(b'default push revset for path evaluates to an empty set')
5636 _(b'default push revset for path evaluates to an empty set')
5635 )
5637 )
5636 elif ui.configbool(b'commands', b'push.require-revs'):
5638 elif ui.configbool(b'commands', b'push.require-revs'):
5637 raise error.Abort(
5639 raise error.Abort(
5638 _(b'no revisions specified to push'),
5640 _(b'no revisions specified to push'),
5639 hint=_(b'did you mean "hg push -r ."?'),
5641 hint=_(b'did you mean "hg push -r ."?'),
5640 )
5642 )
5641
5643
5642 repo._subtoppath = dest
5644 repo._subtoppath = dest
5643 try:
5645 try:
5644 # push subrepos depth-first for coherent ordering
5646 # push subrepos depth-first for coherent ordering
5645 c = repo[b'.']
5647 c = repo[b'.']
5646 subs = c.substate # only repos that are committed
5648 subs = c.substate # only repos that are committed
5647 for s in sorted(subs):
5649 for s in sorted(subs):
5648 result = c.sub(s).push(opts)
5650 result = c.sub(s).push(opts)
5649 if result == 0:
5651 if result == 0:
5650 return not result
5652 return not result
5651 finally:
5653 finally:
5652 del repo._subtoppath
5654 del repo._subtoppath
5653
5655
5654 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5656 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5655 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5657 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5656
5658
5657 pushop = exchange.push(
5659 pushop = exchange.push(
5658 repo,
5660 repo,
5659 other,
5661 other,
5660 opts.get(b'force'),
5662 opts.get(b'force'),
5661 revs=revs,
5663 revs=revs,
5662 newbranch=opts.get(b'new_branch'),
5664 newbranch=opts.get(b'new_branch'),
5663 bookmarks=opts.get(b'bookmark', ()),
5665 bookmarks=opts.get(b'bookmark', ()),
5664 publish=opts.get(b'publish'),
5666 publish=opts.get(b'publish'),
5665 opargs=opargs,
5667 opargs=opargs,
5666 )
5668 )
5667
5669
5668 result = not pushop.cgresult
5670 result = not pushop.cgresult
5669
5671
5670 if pushop.bkresult is not None:
5672 if pushop.bkresult is not None:
5671 if pushop.bkresult == 2:
5673 if pushop.bkresult == 2:
5672 result = 2
5674 result = 2
5673 elif not result and pushop.bkresult:
5675 elif not result and pushop.bkresult:
5674 result = 2
5676 result = 2
5675
5677
5676 return result
5678 return result
5677
5679
5678
5680
5679 @command(
5681 @command(
5680 b'recover',
5682 b'recover',
5681 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5683 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5682 helpcategory=command.CATEGORY_MAINTENANCE,
5684 helpcategory=command.CATEGORY_MAINTENANCE,
5683 )
5685 )
5684 def recover(ui, repo, **opts):
5686 def recover(ui, repo, **opts):
5685 """roll back an interrupted transaction
5687 """roll back an interrupted transaction
5686
5688
5687 Recover from an interrupted commit or pull.
5689 Recover from an interrupted commit or pull.
5688
5690
5689 This command tries to fix the repository status after an
5691 This command tries to fix the repository status after an
5690 interrupted operation. It should only be necessary when Mercurial
5692 interrupted operation. It should only be necessary when Mercurial
5691 suggests it.
5693 suggests it.
5692
5694
5693 Returns 0 if successful, 1 if nothing to recover or verify fails.
5695 Returns 0 if successful, 1 if nothing to recover or verify fails.
5694 """
5696 """
5695 ret = repo.recover()
5697 ret = repo.recover()
5696 if ret:
5698 if ret:
5697 if opts['verify']:
5699 if opts['verify']:
5698 return hg.verify(repo)
5700 return hg.verify(repo)
5699 else:
5701 else:
5700 msg = _(
5702 msg = _(
5701 b"(verify step skipped, run `hg verify` to check your "
5703 b"(verify step skipped, run `hg verify` to check your "
5702 b"repository content)\n"
5704 b"repository content)\n"
5703 )
5705 )
5704 ui.warn(msg)
5706 ui.warn(msg)
5705 return 0
5707 return 0
5706 return 1
5708 return 1
5707
5709
5708
5710
5709 @command(
5711 @command(
5710 b'remove|rm',
5712 b'remove|rm',
5711 [
5713 [
5712 (b'A', b'after', None, _(b'record delete for missing files')),
5714 (b'A', b'after', None, _(b'record delete for missing files')),
5713 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5715 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5714 ]
5716 ]
5715 + subrepoopts
5717 + subrepoopts
5716 + walkopts
5718 + walkopts
5717 + dryrunopts,
5719 + dryrunopts,
5718 _(b'[OPTION]... FILE...'),
5720 _(b'[OPTION]... FILE...'),
5719 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5721 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5720 helpbasic=True,
5722 helpbasic=True,
5721 inferrepo=True,
5723 inferrepo=True,
5722 )
5724 )
5723 def remove(ui, repo, *pats, **opts):
5725 def remove(ui, repo, *pats, **opts):
5724 """remove the specified files on the next commit
5726 """remove the specified files on the next commit
5725
5727
5726 Schedule the indicated files for removal from the current branch.
5728 Schedule the indicated files for removal from the current branch.
5727
5729
5728 This command schedules the files to be removed at the next commit.
5730 This command schedules the files to be removed at the next commit.
5729 To undo a remove before that, see :hg:`revert`. To undo added
5731 To undo a remove before that, see :hg:`revert`. To undo added
5730 files, see :hg:`forget`.
5732 files, see :hg:`forget`.
5731
5733
5732 .. container:: verbose
5734 .. container:: verbose
5733
5735
5734 -A/--after can be used to remove only files that have already
5736 -A/--after can be used to remove only files that have already
5735 been deleted, -f/--force can be used to force deletion, and -Af
5737 been deleted, -f/--force can be used to force deletion, and -Af
5736 can be used to remove files from the next revision without
5738 can be used to remove files from the next revision without
5737 deleting them from the working directory.
5739 deleting them from the working directory.
5738
5740
5739 The following table details the behavior of remove for different
5741 The following table details the behavior of remove for different
5740 file states (columns) and option combinations (rows). The file
5742 file states (columns) and option combinations (rows). The file
5741 states are Added [A], Clean [C], Modified [M] and Missing [!]
5743 states are Added [A], Clean [C], Modified [M] and Missing [!]
5742 (as reported by :hg:`status`). The actions are Warn, Remove
5744 (as reported by :hg:`status`). The actions are Warn, Remove
5743 (from branch) and Delete (from disk):
5745 (from branch) and Delete (from disk):
5744
5746
5745 ========= == == == ==
5747 ========= == == == ==
5746 opt/state A C M !
5748 opt/state A C M !
5747 ========= == == == ==
5749 ========= == == == ==
5748 none W RD W R
5750 none W RD W R
5749 -f R RD RD R
5751 -f R RD RD R
5750 -A W W W R
5752 -A W W W R
5751 -Af R R R R
5753 -Af R R R R
5752 ========= == == == ==
5754 ========= == == == ==
5753
5755
5754 .. note::
5756 .. note::
5755
5757
5756 :hg:`remove` never deletes files in Added [A] state from the
5758 :hg:`remove` never deletes files in Added [A] state from the
5757 working directory, not even if ``--force`` is specified.
5759 working directory, not even if ``--force`` is specified.
5758
5760
5759 Returns 0 on success, 1 if any warnings encountered.
5761 Returns 0 on success, 1 if any warnings encountered.
5760 """
5762 """
5761
5763
5762 opts = pycompat.byteskwargs(opts)
5764 opts = pycompat.byteskwargs(opts)
5763 after, force = opts.get(b'after'), opts.get(b'force')
5765 after, force = opts.get(b'after'), opts.get(b'force')
5764 dryrun = opts.get(b'dry_run')
5766 dryrun = opts.get(b'dry_run')
5765 if not pats and not after:
5767 if not pats and not after:
5766 raise error.Abort(_(b'no files specified'))
5768 raise error.Abort(_(b'no files specified'))
5767
5769
5768 m = scmutil.match(repo[None], pats, opts)
5770 m = scmutil.match(repo[None], pats, opts)
5769 subrepos = opts.get(b'subrepos')
5771 subrepos = opts.get(b'subrepos')
5770 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5772 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5771 return cmdutil.remove(
5773 return cmdutil.remove(
5772 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5774 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5773 )
5775 )
5774
5776
5775
5777
5776 @command(
5778 @command(
5777 b'rename|move|mv',
5779 b'rename|move|mv',
5778 [
5780 [
5779 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5781 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5780 (
5782 (
5781 b'f',
5783 b'f',
5782 b'force',
5784 b'force',
5783 None,
5785 None,
5784 _(b'forcibly move over an existing managed file'),
5786 _(b'forcibly move over an existing managed file'),
5785 ),
5787 ),
5786 ]
5788 ]
5787 + walkopts
5789 + walkopts
5788 + dryrunopts,
5790 + dryrunopts,
5789 _(b'[OPTION]... SOURCE... DEST'),
5791 _(b'[OPTION]... SOURCE... DEST'),
5790 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5791 )
5793 )
5792 def rename(ui, repo, *pats, **opts):
5794 def rename(ui, repo, *pats, **opts):
5793 """rename files; equivalent of copy + remove
5795 """rename files; equivalent of copy + remove
5794
5796
5795 Mark dest as copies of sources; mark sources for deletion. If dest
5797 Mark dest as copies of sources; mark sources for deletion. If dest
5796 is a directory, copies are put in that directory. If dest is a
5798 is a directory, copies are put in that directory. If dest is a
5797 file, there can only be one source.
5799 file, there can only be one source.
5798
5800
5799 By default, this command copies the contents of files as they
5801 By default, this command copies the contents of files as they
5800 exist in the working directory. If invoked with -A/--after, the
5802 exist in the working directory. If invoked with -A/--after, the
5801 operation is recorded, but no copying is performed.
5803 operation is recorded, but no copying is performed.
5802
5804
5803 This command takes effect at the next commit. To undo a rename
5805 This command takes effect at the next commit. To undo a rename
5804 before that, see :hg:`revert`.
5806 before that, see :hg:`revert`.
5805
5807
5806 Returns 0 on success, 1 if errors are encountered.
5808 Returns 0 on success, 1 if errors are encountered.
5807 """
5809 """
5808 opts = pycompat.byteskwargs(opts)
5810 opts = pycompat.byteskwargs(opts)
5809 with repo.wlock(False):
5811 with repo.wlock(False):
5810 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5812 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5811
5813
5812
5814
5813 @command(
5815 @command(
5814 b'resolve',
5816 b'resolve',
5815 [
5817 [
5816 (b'a', b'all', None, _(b'select all unresolved files')),
5818 (b'a', b'all', None, _(b'select all unresolved files')),
5817 (b'l', b'list', None, _(b'list state of files needing merge')),
5819 (b'l', b'list', None, _(b'list state of files needing merge')),
5818 (b'm', b'mark', None, _(b'mark files as resolved')),
5820 (b'm', b'mark', None, _(b'mark files as resolved')),
5819 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5821 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5820 (b'n', b'no-status', None, _(b'hide status prefix')),
5822 (b'n', b'no-status', None, _(b'hide status prefix')),
5821 (b'', b're-merge', None, _(b're-merge files')),
5823 (b'', b're-merge', None, _(b're-merge files')),
5822 ]
5824 ]
5823 + mergetoolopts
5825 + mergetoolopts
5824 + walkopts
5826 + walkopts
5825 + formatteropts,
5827 + formatteropts,
5826 _(b'[OPTION]... [FILE]...'),
5828 _(b'[OPTION]... [FILE]...'),
5827 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5829 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5828 inferrepo=True,
5830 inferrepo=True,
5829 )
5831 )
5830 def resolve(ui, repo, *pats, **opts):
5832 def resolve(ui, repo, *pats, **opts):
5831 """redo merges or set/view the merge status of files
5833 """redo merges or set/view the merge status of files
5832
5834
5833 Merges with unresolved conflicts are often the result of
5835 Merges with unresolved conflicts are often the result of
5834 non-interactive merging using the ``internal:merge`` configuration
5836 non-interactive merging using the ``internal:merge`` configuration
5835 setting, or a command-line merge tool like ``diff3``. The resolve
5837 setting, or a command-line merge tool like ``diff3``. The resolve
5836 command is used to manage the files involved in a merge, after
5838 command is used to manage the files involved in a merge, after
5837 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5839 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5838 working directory must have two parents). See :hg:`help
5840 working directory must have two parents). See :hg:`help
5839 merge-tools` for information on configuring merge tools.
5841 merge-tools` for information on configuring merge tools.
5840
5842
5841 The resolve command can be used in the following ways:
5843 The resolve command can be used in the following ways:
5842
5844
5843 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5845 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5844 the specified files, discarding any previous merge attempts. Re-merging
5846 the specified files, discarding any previous merge attempts. Re-merging
5845 is not performed for files already marked as resolved. Use ``--all/-a``
5847 is not performed for files already marked as resolved. Use ``--all/-a``
5846 to select all unresolved files. ``--tool`` can be used to specify
5848 to select all unresolved files. ``--tool`` can be used to specify
5847 the merge tool used for the given files. It overrides the HGMERGE
5849 the merge tool used for the given files. It overrides the HGMERGE
5848 environment variable and your configuration files. Previous file
5850 environment variable and your configuration files. Previous file
5849 contents are saved with a ``.orig`` suffix.
5851 contents are saved with a ``.orig`` suffix.
5850
5852
5851 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5853 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5852 (e.g. after having manually fixed-up the files). The default is
5854 (e.g. after having manually fixed-up the files). The default is
5853 to mark all unresolved files.
5855 to mark all unresolved files.
5854
5856
5855 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5857 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5856 default is to mark all resolved files.
5858 default is to mark all resolved files.
5857
5859
5858 - :hg:`resolve -l`: list files which had or still have conflicts.
5860 - :hg:`resolve -l`: list files which had or still have conflicts.
5859 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5861 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5860 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5862 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5861 the list. See :hg:`help filesets` for details.
5863 the list. See :hg:`help filesets` for details.
5862
5864
5863 .. note::
5865 .. note::
5864
5866
5865 Mercurial will not let you commit files with unresolved merge
5867 Mercurial will not let you commit files with unresolved merge
5866 conflicts. You must use :hg:`resolve -m ...` before you can
5868 conflicts. You must use :hg:`resolve -m ...` before you can
5867 commit after a conflicting merge.
5869 commit after a conflicting merge.
5868
5870
5869 .. container:: verbose
5871 .. container:: verbose
5870
5872
5871 Template:
5873 Template:
5872
5874
5873 The following keywords are supported in addition to the common template
5875 The following keywords are supported in addition to the common template
5874 keywords and functions. See also :hg:`help templates`.
5876 keywords and functions. See also :hg:`help templates`.
5875
5877
5876 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5878 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5877 :path: String. Repository-absolute path of the file.
5879 :path: String. Repository-absolute path of the file.
5878
5880
5879 Returns 0 on success, 1 if any files fail a resolve attempt.
5881 Returns 0 on success, 1 if any files fail a resolve attempt.
5880 """
5882 """
5881
5883
5882 opts = pycompat.byteskwargs(opts)
5884 opts = pycompat.byteskwargs(opts)
5883 confirm = ui.configbool(b'commands', b'resolve.confirm')
5885 confirm = ui.configbool(b'commands', b'resolve.confirm')
5884 flaglist = b'all mark unmark list no_status re_merge'.split()
5886 flaglist = b'all mark unmark list no_status re_merge'.split()
5885 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5887 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5886
5888
5887 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5889 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5888 if actioncount > 1:
5890 if actioncount > 1:
5889 raise error.Abort(_(b"too many actions specified"))
5891 raise error.Abort(_(b"too many actions specified"))
5890 elif actioncount == 0 and ui.configbool(
5892 elif actioncount == 0 and ui.configbool(
5891 b'commands', b'resolve.explicit-re-merge'
5893 b'commands', b'resolve.explicit-re-merge'
5892 ):
5894 ):
5893 hint = _(b'use --mark, --unmark, --list or --re-merge')
5895 hint = _(b'use --mark, --unmark, --list or --re-merge')
5894 raise error.Abort(_(b'no action specified'), hint=hint)
5896 raise error.Abort(_(b'no action specified'), hint=hint)
5895 if pats and all:
5897 if pats and all:
5896 raise error.Abort(_(b"can't specify --all and patterns"))
5898 raise error.Abort(_(b"can't specify --all and patterns"))
5897 if not (all or pats or show or mark or unmark):
5899 if not (all or pats or show or mark or unmark):
5898 raise error.Abort(
5900 raise error.Abort(
5899 _(b'no files or directories specified'),
5901 _(b'no files or directories specified'),
5900 hint=b'use --all to re-merge all unresolved files',
5902 hint=b'use --all to re-merge all unresolved files',
5901 )
5903 )
5902
5904
5903 if confirm:
5905 if confirm:
5904 if all:
5906 if all:
5905 if ui.promptchoice(
5907 if ui.promptchoice(
5906 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5908 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5907 ):
5909 ):
5908 raise error.Abort(_(b'user quit'))
5910 raise error.Abort(_(b'user quit'))
5909 if mark and not pats:
5911 if mark and not pats:
5910 if ui.promptchoice(
5912 if ui.promptchoice(
5911 _(
5913 _(
5912 b'mark all unresolved files as resolved (yn)?'
5914 b'mark all unresolved files as resolved (yn)?'
5913 b'$$ &Yes $$ &No'
5915 b'$$ &Yes $$ &No'
5914 )
5916 )
5915 ):
5917 ):
5916 raise error.Abort(_(b'user quit'))
5918 raise error.Abort(_(b'user quit'))
5917 if unmark and not pats:
5919 if unmark and not pats:
5918 if ui.promptchoice(
5920 if ui.promptchoice(
5919 _(
5921 _(
5920 b'mark all resolved files as unresolved (yn)?'
5922 b'mark all resolved files as unresolved (yn)?'
5921 b'$$ &Yes $$ &No'
5923 b'$$ &Yes $$ &No'
5922 )
5924 )
5923 ):
5925 ):
5924 raise error.Abort(_(b'user quit'))
5926 raise error.Abort(_(b'user quit'))
5925
5927
5926 uipathfn = scmutil.getuipathfn(repo)
5928 uipathfn = scmutil.getuipathfn(repo)
5927
5929
5928 if show:
5930 if show:
5929 ui.pager(b'resolve')
5931 ui.pager(b'resolve')
5930 fm = ui.formatter(b'resolve', opts)
5932 fm = ui.formatter(b'resolve', opts)
5931 ms = mergemod.mergestate.read(repo)
5933 ms = mergemod.mergestate.read(repo)
5932 wctx = repo[None]
5934 wctx = repo[None]
5933 m = scmutil.match(wctx, pats, opts)
5935 m = scmutil.match(wctx, pats, opts)
5934
5936
5935 # Labels and keys based on merge state. Unresolved path conflicts show
5937 # Labels and keys based on merge state. Unresolved path conflicts show
5936 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5938 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5937 # resolved conflicts.
5939 # resolved conflicts.
5938 mergestateinfo = {
5940 mergestateinfo = {
5939 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5941 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5940 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5942 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5941 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5943 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5942 b'resolve.unresolved',
5944 b'resolve.unresolved',
5943 b'P',
5945 b'P',
5944 ),
5946 ),
5945 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5947 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5946 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5948 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5947 b'resolve.driverresolved',
5949 b'resolve.driverresolved',
5948 b'D',
5950 b'D',
5949 ),
5951 ),
5950 }
5952 }
5951
5953
5952 for f in ms:
5954 for f in ms:
5953 if not m(f):
5955 if not m(f):
5954 continue
5956 continue
5955
5957
5956 label, key = mergestateinfo[ms[f]]
5958 label, key = mergestateinfo[ms[f]]
5957 fm.startitem()
5959 fm.startitem()
5958 fm.context(ctx=wctx)
5960 fm.context(ctx=wctx)
5959 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5961 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5960 fm.data(path=f)
5962 fm.data(path=f)
5961 fm.plain(b'%s\n' % uipathfn(f), label=label)
5963 fm.plain(b'%s\n' % uipathfn(f), label=label)
5962 fm.end()
5964 fm.end()
5963 return 0
5965 return 0
5964
5966
5965 with repo.wlock():
5967 with repo.wlock():
5966 ms = mergemod.mergestate.read(repo)
5968 ms = mergemod.mergestate.read(repo)
5967
5969
5968 if not (ms.active() or repo.dirstate.p2() != nullid):
5970 if not (ms.active() or repo.dirstate.p2() != nullid):
5969 raise error.Abort(
5971 raise error.Abort(
5970 _(b'resolve command not applicable when not merging')
5972 _(b'resolve command not applicable when not merging')
5971 )
5973 )
5972
5974
5973 wctx = repo[None]
5975 wctx = repo[None]
5974
5976
5975 if (
5977 if (
5976 ms.mergedriver
5978 ms.mergedriver
5977 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5979 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5978 ):
5980 ):
5979 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5981 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5980 ms.commit()
5982 ms.commit()
5981 # allow mark and unmark to go through
5983 # allow mark and unmark to go through
5982 if not mark and not unmark and not proceed:
5984 if not mark and not unmark and not proceed:
5983 return 1
5985 return 1
5984
5986
5985 m = scmutil.match(wctx, pats, opts)
5987 m = scmutil.match(wctx, pats, opts)
5986 ret = 0
5988 ret = 0
5987 didwork = False
5989 didwork = False
5988 runconclude = False
5990 runconclude = False
5989
5991
5990 tocomplete = []
5992 tocomplete = []
5991 hasconflictmarkers = []
5993 hasconflictmarkers = []
5992 if mark:
5994 if mark:
5993 markcheck = ui.config(b'commands', b'resolve.mark-check')
5995 markcheck = ui.config(b'commands', b'resolve.mark-check')
5994 if markcheck not in [b'warn', b'abort']:
5996 if markcheck not in [b'warn', b'abort']:
5995 # Treat all invalid / unrecognized values as 'none'.
5997 # Treat all invalid / unrecognized values as 'none'.
5996 markcheck = False
5998 markcheck = False
5997 for f in ms:
5999 for f in ms:
5998 if not m(f):
6000 if not m(f):
5999 continue
6001 continue
6000
6002
6001 didwork = True
6003 didwork = True
6002
6004
6003 # don't let driver-resolved files be marked, and run the conclude
6005 # don't let driver-resolved files be marked, and run the conclude
6004 # step if asked to resolve
6006 # step if asked to resolve
6005 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6007 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6006 exact = m.exact(f)
6008 exact = m.exact(f)
6007 if mark:
6009 if mark:
6008 if exact:
6010 if exact:
6009 ui.warn(
6011 ui.warn(
6010 _(b'not marking %s as it is driver-resolved\n')
6012 _(b'not marking %s as it is driver-resolved\n')
6011 % uipathfn(f)
6013 % uipathfn(f)
6012 )
6014 )
6013 elif unmark:
6015 elif unmark:
6014 if exact:
6016 if exact:
6015 ui.warn(
6017 ui.warn(
6016 _(b'not unmarking %s as it is driver-resolved\n')
6018 _(b'not unmarking %s as it is driver-resolved\n')
6017 % uipathfn(f)
6019 % uipathfn(f)
6018 )
6020 )
6019 else:
6021 else:
6020 runconclude = True
6022 runconclude = True
6021 continue
6023 continue
6022
6024
6023 # path conflicts must be resolved manually
6025 # path conflicts must be resolved manually
6024 if ms[f] in (
6026 if ms[f] in (
6025 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6027 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6026 mergemod.MERGE_RECORD_RESOLVED_PATH,
6028 mergemod.MERGE_RECORD_RESOLVED_PATH,
6027 ):
6029 ):
6028 if mark:
6030 if mark:
6029 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6031 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6030 elif unmark:
6032 elif unmark:
6031 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6033 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6032 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6034 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6033 ui.warn(
6035 ui.warn(
6034 _(b'%s: path conflict must be resolved manually\n')
6036 _(b'%s: path conflict must be resolved manually\n')
6035 % uipathfn(f)
6037 % uipathfn(f)
6036 )
6038 )
6037 continue
6039 continue
6038
6040
6039 if mark:
6041 if mark:
6040 if markcheck:
6042 if markcheck:
6041 fdata = repo.wvfs.tryread(f)
6043 fdata = repo.wvfs.tryread(f)
6042 if (
6044 if (
6043 filemerge.hasconflictmarkers(fdata)
6045 filemerge.hasconflictmarkers(fdata)
6044 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6046 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6045 ):
6047 ):
6046 hasconflictmarkers.append(f)
6048 hasconflictmarkers.append(f)
6047 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6049 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6048 elif unmark:
6050 elif unmark:
6049 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6051 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6050 else:
6052 else:
6051 # backup pre-resolve (merge uses .orig for its own purposes)
6053 # backup pre-resolve (merge uses .orig for its own purposes)
6052 a = repo.wjoin(f)
6054 a = repo.wjoin(f)
6053 try:
6055 try:
6054 util.copyfile(a, a + b".resolve")
6056 util.copyfile(a, a + b".resolve")
6055 except (IOError, OSError) as inst:
6057 except (IOError, OSError) as inst:
6056 if inst.errno != errno.ENOENT:
6058 if inst.errno != errno.ENOENT:
6057 raise
6059 raise
6058
6060
6059 try:
6061 try:
6060 # preresolve file
6062 # preresolve file
6061 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6063 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6062 with ui.configoverride(overrides, b'resolve'):
6064 with ui.configoverride(overrides, b'resolve'):
6063 complete, r = ms.preresolve(f, wctx)
6065 complete, r = ms.preresolve(f, wctx)
6064 if not complete:
6066 if not complete:
6065 tocomplete.append(f)
6067 tocomplete.append(f)
6066 elif r:
6068 elif r:
6067 ret = 1
6069 ret = 1
6068 finally:
6070 finally:
6069 ms.commit()
6071 ms.commit()
6070
6072
6071 # replace filemerge's .orig file with our resolve file, but only
6073 # replace filemerge's .orig file with our resolve file, but only
6072 # for merges that are complete
6074 # for merges that are complete
6073 if complete:
6075 if complete:
6074 try:
6076 try:
6075 util.rename(
6077 util.rename(
6076 a + b".resolve", scmutil.backuppath(ui, repo, f)
6078 a + b".resolve", scmutil.backuppath(ui, repo, f)
6077 )
6079 )
6078 except OSError as inst:
6080 except OSError as inst:
6079 if inst.errno != errno.ENOENT:
6081 if inst.errno != errno.ENOENT:
6080 raise
6082 raise
6081
6083
6082 if hasconflictmarkers:
6084 if hasconflictmarkers:
6083 ui.warn(
6085 ui.warn(
6084 _(
6086 _(
6085 b'warning: the following files still have conflict '
6087 b'warning: the following files still have conflict '
6086 b'markers:\n'
6088 b'markers:\n'
6087 )
6089 )
6088 + b''.join(
6090 + b''.join(
6089 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6091 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6090 )
6092 )
6091 )
6093 )
6092 if markcheck == b'abort' and not all and not pats:
6094 if markcheck == b'abort' and not all and not pats:
6093 raise error.Abort(
6095 raise error.Abort(
6094 _(b'conflict markers detected'),
6096 _(b'conflict markers detected'),
6095 hint=_(b'use --all to mark anyway'),
6097 hint=_(b'use --all to mark anyway'),
6096 )
6098 )
6097
6099
6098 for f in tocomplete:
6100 for f in tocomplete:
6099 try:
6101 try:
6100 # resolve file
6102 # resolve file
6101 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6103 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6102 with ui.configoverride(overrides, b'resolve'):
6104 with ui.configoverride(overrides, b'resolve'):
6103 r = ms.resolve(f, wctx)
6105 r = ms.resolve(f, wctx)
6104 if r:
6106 if r:
6105 ret = 1
6107 ret = 1
6106 finally:
6108 finally:
6107 ms.commit()
6109 ms.commit()
6108
6110
6109 # replace filemerge's .orig file with our resolve file
6111 # replace filemerge's .orig file with our resolve file
6110 a = repo.wjoin(f)
6112 a = repo.wjoin(f)
6111 try:
6113 try:
6112 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6114 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6113 except OSError as inst:
6115 except OSError as inst:
6114 if inst.errno != errno.ENOENT:
6116 if inst.errno != errno.ENOENT:
6115 raise
6117 raise
6116
6118
6117 ms.commit()
6119 ms.commit()
6118 ms.recordactions()
6120 ms.recordactions()
6119
6121
6120 if not didwork and pats:
6122 if not didwork and pats:
6121 hint = None
6123 hint = None
6122 if not any([p for p in pats if p.find(b':') >= 0]):
6124 if not any([p for p in pats if p.find(b':') >= 0]):
6123 pats = [b'path:%s' % p for p in pats]
6125 pats = [b'path:%s' % p for p in pats]
6124 m = scmutil.match(wctx, pats, opts)
6126 m = scmutil.match(wctx, pats, opts)
6125 for f in ms:
6127 for f in ms:
6126 if not m(f):
6128 if not m(f):
6127 continue
6129 continue
6128
6130
6129 def flag(o):
6131 def flag(o):
6130 if o == b're_merge':
6132 if o == b're_merge':
6131 return b'--re-merge '
6133 return b'--re-merge '
6132 return b'-%s ' % o[0:1]
6134 return b'-%s ' % o[0:1]
6133
6135
6134 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6136 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6135 hint = _(b"(try: hg resolve %s%s)\n") % (
6137 hint = _(b"(try: hg resolve %s%s)\n") % (
6136 flags,
6138 flags,
6137 b' '.join(pats),
6139 b' '.join(pats),
6138 )
6140 )
6139 break
6141 break
6140 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6142 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6141 if hint:
6143 if hint:
6142 ui.warn(hint)
6144 ui.warn(hint)
6143 elif ms.mergedriver and ms.mdstate() != b's':
6145 elif ms.mergedriver and ms.mdstate() != b's':
6144 # run conclude step when either a driver-resolved file is requested
6146 # run conclude step when either a driver-resolved file is requested
6145 # or there are no driver-resolved files
6147 # or there are no driver-resolved files
6146 # we can't use 'ret' to determine whether any files are unresolved
6148 # we can't use 'ret' to determine whether any files are unresolved
6147 # because we might not have tried to resolve some
6149 # because we might not have tried to resolve some
6148 if (runconclude or not list(ms.driverresolved())) and not list(
6150 if (runconclude or not list(ms.driverresolved())) and not list(
6149 ms.unresolved()
6151 ms.unresolved()
6150 ):
6152 ):
6151 proceed = mergemod.driverconclude(repo, ms, wctx)
6153 proceed = mergemod.driverconclude(repo, ms, wctx)
6152 ms.commit()
6154 ms.commit()
6153 if not proceed:
6155 if not proceed:
6154 return 1
6156 return 1
6155
6157
6156 # Nudge users into finishing an unfinished operation
6158 # Nudge users into finishing an unfinished operation
6157 unresolvedf = list(ms.unresolved())
6159 unresolvedf = list(ms.unresolved())
6158 driverresolvedf = list(ms.driverresolved())
6160 driverresolvedf = list(ms.driverresolved())
6159 if not unresolvedf and not driverresolvedf:
6161 if not unresolvedf and not driverresolvedf:
6160 ui.status(_(b'(no more unresolved files)\n'))
6162 ui.status(_(b'(no more unresolved files)\n'))
6161 cmdutil.checkafterresolved(repo)
6163 cmdutil.checkafterresolved(repo)
6162 elif not unresolvedf:
6164 elif not unresolvedf:
6163 ui.status(
6165 ui.status(
6164 _(
6166 _(
6165 b'(no more unresolved files -- '
6167 b'(no more unresolved files -- '
6166 b'run "hg resolve --all" to conclude)\n'
6168 b'run "hg resolve --all" to conclude)\n'
6167 )
6169 )
6168 )
6170 )
6169
6171
6170 return ret
6172 return ret
6171
6173
6172
6174
6173 @command(
6175 @command(
6174 b'revert',
6176 b'revert',
6175 [
6177 [
6176 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6178 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6177 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6179 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6178 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6180 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6179 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6181 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6180 (b'i', b'interactive', None, _(b'interactively select the changes')),
6182 (b'i', b'interactive', None, _(b'interactively select the changes')),
6181 ]
6183 ]
6182 + walkopts
6184 + walkopts
6183 + dryrunopts,
6185 + dryrunopts,
6184 _(b'[OPTION]... [-r REV] [NAME]...'),
6186 _(b'[OPTION]... [-r REV] [NAME]...'),
6185 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6187 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6186 )
6188 )
6187 def revert(ui, repo, *pats, **opts):
6189 def revert(ui, repo, *pats, **opts):
6188 """restore files to their checkout state
6190 """restore files to their checkout state
6189
6191
6190 .. note::
6192 .. note::
6191
6193
6192 To check out earlier revisions, you should use :hg:`update REV`.
6194 To check out earlier revisions, you should use :hg:`update REV`.
6193 To cancel an uncommitted merge (and lose your changes),
6195 To cancel an uncommitted merge (and lose your changes),
6194 use :hg:`merge --abort`.
6196 use :hg:`merge --abort`.
6195
6197
6196 With no revision specified, revert the specified files or directories
6198 With no revision specified, revert the specified files or directories
6197 to the contents they had in the parent of the working directory.
6199 to the contents they had in the parent of the working directory.
6198 This restores the contents of files to an unmodified
6200 This restores the contents of files to an unmodified
6199 state and unschedules adds, removes, copies, and renames. If the
6201 state and unschedules adds, removes, copies, and renames. If the
6200 working directory has two parents, you must explicitly specify a
6202 working directory has two parents, you must explicitly specify a
6201 revision.
6203 revision.
6202
6204
6203 Using the -r/--rev or -d/--date options, revert the given files or
6205 Using the -r/--rev or -d/--date options, revert the given files or
6204 directories to their states as of a specific revision. Because
6206 directories to their states as of a specific revision. Because
6205 revert does not change the working directory parents, this will
6207 revert does not change the working directory parents, this will
6206 cause these files to appear modified. This can be helpful to "back
6208 cause these files to appear modified. This can be helpful to "back
6207 out" some or all of an earlier change. See :hg:`backout` for a
6209 out" some or all of an earlier change. See :hg:`backout` for a
6208 related method.
6210 related method.
6209
6211
6210 Modified files are saved with a .orig suffix before reverting.
6212 Modified files are saved with a .orig suffix before reverting.
6211 To disable these backups, use --no-backup. It is possible to store
6213 To disable these backups, use --no-backup. It is possible to store
6212 the backup files in a custom directory relative to the root of the
6214 the backup files in a custom directory relative to the root of the
6213 repository by setting the ``ui.origbackuppath`` configuration
6215 repository by setting the ``ui.origbackuppath`` configuration
6214 option.
6216 option.
6215
6217
6216 See :hg:`help dates` for a list of formats valid for -d/--date.
6218 See :hg:`help dates` for a list of formats valid for -d/--date.
6217
6219
6218 See :hg:`help backout` for a way to reverse the effect of an
6220 See :hg:`help backout` for a way to reverse the effect of an
6219 earlier changeset.
6221 earlier changeset.
6220
6222
6221 Returns 0 on success.
6223 Returns 0 on success.
6222 """
6224 """
6223
6225
6224 opts = pycompat.byteskwargs(opts)
6226 opts = pycompat.byteskwargs(opts)
6225 if opts.get(b"date"):
6227 if opts.get(b"date"):
6226 if opts.get(b"rev"):
6228 if opts.get(b"rev"):
6227 raise error.Abort(_(b"you can't specify a revision and a date"))
6229 raise error.Abort(_(b"you can't specify a revision and a date"))
6228 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6230 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6229
6231
6230 parent, p2 = repo.dirstate.parents()
6232 parent, p2 = repo.dirstate.parents()
6231 if not opts.get(b'rev') and p2 != nullid:
6233 if not opts.get(b'rev') and p2 != nullid:
6232 # revert after merge is a trap for new users (issue2915)
6234 # revert after merge is a trap for new users (issue2915)
6233 raise error.Abort(
6235 raise error.Abort(
6234 _(b'uncommitted merge with no revision specified'),
6236 _(b'uncommitted merge with no revision specified'),
6235 hint=_(b"use 'hg update' or see 'hg help revert'"),
6237 hint=_(b"use 'hg update' or see 'hg help revert'"),
6236 )
6238 )
6237
6239
6238 rev = opts.get(b'rev')
6240 rev = opts.get(b'rev')
6239 if rev:
6241 if rev:
6240 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6242 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6241 ctx = scmutil.revsingle(repo, rev)
6243 ctx = scmutil.revsingle(repo, rev)
6242
6244
6243 if not (
6245 if not (
6244 pats
6246 pats
6245 or opts.get(b'include')
6247 or opts.get(b'include')
6246 or opts.get(b'exclude')
6248 or opts.get(b'exclude')
6247 or opts.get(b'all')
6249 or opts.get(b'all')
6248 or opts.get(b'interactive')
6250 or opts.get(b'interactive')
6249 ):
6251 ):
6250 msg = _(b"no files or directories specified")
6252 msg = _(b"no files or directories specified")
6251 if p2 != nullid:
6253 if p2 != nullid:
6252 hint = _(
6254 hint = _(
6253 b"uncommitted merge, use --all to discard all changes,"
6255 b"uncommitted merge, use --all to discard all changes,"
6254 b" or 'hg update -C .' to abort the merge"
6256 b" or 'hg update -C .' to abort the merge"
6255 )
6257 )
6256 raise error.Abort(msg, hint=hint)
6258 raise error.Abort(msg, hint=hint)
6257 dirty = any(repo.status())
6259 dirty = any(repo.status())
6258 node = ctx.node()
6260 node = ctx.node()
6259 if node != parent:
6261 if node != parent:
6260 if dirty:
6262 if dirty:
6261 hint = (
6263 hint = (
6262 _(
6264 _(
6263 b"uncommitted changes, use --all to discard all"
6265 b"uncommitted changes, use --all to discard all"
6264 b" changes, or 'hg update %d' to update"
6266 b" changes, or 'hg update %d' to update"
6265 )
6267 )
6266 % ctx.rev()
6268 % ctx.rev()
6267 )
6269 )
6268 else:
6270 else:
6269 hint = (
6271 hint = (
6270 _(
6272 _(
6271 b"use --all to revert all files,"
6273 b"use --all to revert all files,"
6272 b" or 'hg update %d' to update"
6274 b" or 'hg update %d' to update"
6273 )
6275 )
6274 % ctx.rev()
6276 % ctx.rev()
6275 )
6277 )
6276 elif dirty:
6278 elif dirty:
6277 hint = _(b"uncommitted changes, use --all to discard all changes")
6279 hint = _(b"uncommitted changes, use --all to discard all changes")
6278 else:
6280 else:
6279 hint = _(b"use --all to revert all files")
6281 hint = _(b"use --all to revert all files")
6280 raise error.Abort(msg, hint=hint)
6282 raise error.Abort(msg, hint=hint)
6281
6283
6282 return cmdutil.revert(
6284 return cmdutil.revert(
6283 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6285 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6284 )
6286 )
6285
6287
6286
6288
6287 @command(
6289 @command(
6288 b'rollback',
6290 b'rollback',
6289 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6291 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6290 helpcategory=command.CATEGORY_MAINTENANCE,
6292 helpcategory=command.CATEGORY_MAINTENANCE,
6291 )
6293 )
6292 def rollback(ui, repo, **opts):
6294 def rollback(ui, repo, **opts):
6293 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6295 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6294
6296
6295 Please use :hg:`commit --amend` instead of rollback to correct
6297 Please use :hg:`commit --amend` instead of rollback to correct
6296 mistakes in the last commit.
6298 mistakes in the last commit.
6297
6299
6298 This command should be used with care. There is only one level of
6300 This command should be used with care. There is only one level of
6299 rollback, and there is no way to undo a rollback. It will also
6301 rollback, and there is no way to undo a rollback. It will also
6300 restore the dirstate at the time of the last transaction, losing
6302 restore the dirstate at the time of the last transaction, losing
6301 any dirstate changes since that time. This command does not alter
6303 any dirstate changes since that time. This command does not alter
6302 the working directory.
6304 the working directory.
6303
6305
6304 Transactions are used to encapsulate the effects of all commands
6306 Transactions are used to encapsulate the effects of all commands
6305 that create new changesets or propagate existing changesets into a
6307 that create new changesets or propagate existing changesets into a
6306 repository.
6308 repository.
6307
6309
6308 .. container:: verbose
6310 .. container:: verbose
6309
6311
6310 For example, the following commands are transactional, and their
6312 For example, the following commands are transactional, and their
6311 effects can be rolled back:
6313 effects can be rolled back:
6312
6314
6313 - commit
6315 - commit
6314 - import
6316 - import
6315 - pull
6317 - pull
6316 - push (with this repository as the destination)
6318 - push (with this repository as the destination)
6317 - unbundle
6319 - unbundle
6318
6320
6319 To avoid permanent data loss, rollback will refuse to rollback a
6321 To avoid permanent data loss, rollback will refuse to rollback a
6320 commit transaction if it isn't checked out. Use --force to
6322 commit transaction if it isn't checked out. Use --force to
6321 override this protection.
6323 override this protection.
6322
6324
6323 The rollback command can be entirely disabled by setting the
6325 The rollback command can be entirely disabled by setting the
6324 ``ui.rollback`` configuration setting to false. If you're here
6326 ``ui.rollback`` configuration setting to false. If you're here
6325 because you want to use rollback and it's disabled, you can
6327 because you want to use rollback and it's disabled, you can
6326 re-enable the command by setting ``ui.rollback`` to true.
6328 re-enable the command by setting ``ui.rollback`` to true.
6327
6329
6328 This command is not intended for use on public repositories. Once
6330 This command is not intended for use on public repositories. Once
6329 changes are visible for pull by other users, rolling a transaction
6331 changes are visible for pull by other users, rolling a transaction
6330 back locally is ineffective (someone else may already have pulled
6332 back locally is ineffective (someone else may already have pulled
6331 the changes). Furthermore, a race is possible with readers of the
6333 the changes). Furthermore, a race is possible with readers of the
6332 repository; for example an in-progress pull from the repository
6334 repository; for example an in-progress pull from the repository
6333 may fail if a rollback is performed.
6335 may fail if a rollback is performed.
6334
6336
6335 Returns 0 on success, 1 if no rollback data is available.
6337 Returns 0 on success, 1 if no rollback data is available.
6336 """
6338 """
6337 if not ui.configbool(b'ui', b'rollback'):
6339 if not ui.configbool(b'ui', b'rollback'):
6338 raise error.Abort(
6340 raise error.Abort(
6339 _(b'rollback is disabled because it is unsafe'),
6341 _(b'rollback is disabled because it is unsafe'),
6340 hint=b'see `hg help -v rollback` for information',
6342 hint=b'see `hg help -v rollback` for information',
6341 )
6343 )
6342 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6344 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6343
6345
6344
6346
6345 @command(
6347 @command(
6346 b'root',
6348 b'root',
6347 [] + formatteropts,
6349 [] + formatteropts,
6348 intents={INTENT_READONLY},
6350 intents={INTENT_READONLY},
6349 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6351 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6350 )
6352 )
6351 def root(ui, repo, **opts):
6353 def root(ui, repo, **opts):
6352 """print the root (top) of the current working directory
6354 """print the root (top) of the current working directory
6353
6355
6354 Print the root directory of the current repository.
6356 Print the root directory of the current repository.
6355
6357
6356 .. container:: verbose
6358 .. container:: verbose
6357
6359
6358 Template:
6360 Template:
6359
6361
6360 The following keywords are supported in addition to the common template
6362 The following keywords are supported in addition to the common template
6361 keywords and functions. See also :hg:`help templates`.
6363 keywords and functions. See also :hg:`help templates`.
6362
6364
6363 :hgpath: String. Path to the .hg directory.
6365 :hgpath: String. Path to the .hg directory.
6364 :storepath: String. Path to the directory holding versioned data.
6366 :storepath: String. Path to the directory holding versioned data.
6365
6367
6366 Returns 0 on success.
6368 Returns 0 on success.
6367 """
6369 """
6368 opts = pycompat.byteskwargs(opts)
6370 opts = pycompat.byteskwargs(opts)
6369 with ui.formatter(b'root', opts) as fm:
6371 with ui.formatter(b'root', opts) as fm:
6370 fm.startitem()
6372 fm.startitem()
6371 fm.write(b'reporoot', b'%s\n', repo.root)
6373 fm.write(b'reporoot', b'%s\n', repo.root)
6372 fm.data(hgpath=repo.path, storepath=repo.spath)
6374 fm.data(hgpath=repo.path, storepath=repo.spath)
6373
6375
6374
6376
6375 @command(
6377 @command(
6376 b'serve',
6378 b'serve',
6377 [
6379 [
6378 (
6380 (
6379 b'A',
6381 b'A',
6380 b'accesslog',
6382 b'accesslog',
6381 b'',
6383 b'',
6382 _(b'name of access log file to write to'),
6384 _(b'name of access log file to write to'),
6383 _(b'FILE'),
6385 _(b'FILE'),
6384 ),
6386 ),
6385 (b'd', b'daemon', None, _(b'run server in background')),
6387 (b'd', b'daemon', None, _(b'run server in background')),
6386 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6388 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6387 (
6389 (
6388 b'E',
6390 b'E',
6389 b'errorlog',
6391 b'errorlog',
6390 b'',
6392 b'',
6391 _(b'name of error log file to write to'),
6393 _(b'name of error log file to write to'),
6392 _(b'FILE'),
6394 _(b'FILE'),
6393 ),
6395 ),
6394 # use string type, then we can check if something was passed
6396 # use string type, then we can check if something was passed
6395 (
6397 (
6396 b'p',
6398 b'p',
6397 b'port',
6399 b'port',
6398 b'',
6400 b'',
6399 _(b'port to listen on (default: 8000)'),
6401 _(b'port to listen on (default: 8000)'),
6400 _(b'PORT'),
6402 _(b'PORT'),
6401 ),
6403 ),
6402 (
6404 (
6403 b'a',
6405 b'a',
6404 b'address',
6406 b'address',
6405 b'',
6407 b'',
6406 _(b'address to listen on (default: all interfaces)'),
6408 _(b'address to listen on (default: all interfaces)'),
6407 _(b'ADDR'),
6409 _(b'ADDR'),
6408 ),
6410 ),
6409 (
6411 (
6410 b'',
6412 b'',
6411 b'prefix',
6413 b'prefix',
6412 b'',
6414 b'',
6413 _(b'prefix path to serve from (default: server root)'),
6415 _(b'prefix path to serve from (default: server root)'),
6414 _(b'PREFIX'),
6416 _(b'PREFIX'),
6415 ),
6417 ),
6416 (
6418 (
6417 b'n',
6419 b'n',
6418 b'name',
6420 b'name',
6419 b'',
6421 b'',
6420 _(b'name to show in web pages (default: working directory)'),
6422 _(b'name to show in web pages (default: working directory)'),
6421 _(b'NAME'),
6423 _(b'NAME'),
6422 ),
6424 ),
6423 (
6425 (
6424 b'',
6426 b'',
6425 b'web-conf',
6427 b'web-conf',
6426 b'',
6428 b'',
6427 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6429 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6428 _(b'FILE'),
6430 _(b'FILE'),
6429 ),
6431 ),
6430 (
6432 (
6431 b'',
6433 b'',
6432 b'webdir-conf',
6434 b'webdir-conf',
6433 b'',
6435 b'',
6434 _(b'name of the hgweb config file (DEPRECATED)'),
6436 _(b'name of the hgweb config file (DEPRECATED)'),
6435 _(b'FILE'),
6437 _(b'FILE'),
6436 ),
6438 ),
6437 (
6439 (
6438 b'',
6440 b'',
6439 b'pid-file',
6441 b'pid-file',
6440 b'',
6442 b'',
6441 _(b'name of file to write process ID to'),
6443 _(b'name of file to write process ID to'),
6442 _(b'FILE'),
6444 _(b'FILE'),
6443 ),
6445 ),
6444 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6446 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6445 (
6447 (
6446 b'',
6448 b'',
6447 b'cmdserver',
6449 b'cmdserver',
6448 b'',
6450 b'',
6449 _(b'for remote clients (ADVANCED)'),
6451 _(b'for remote clients (ADVANCED)'),
6450 _(b'MODE'),
6452 _(b'MODE'),
6451 ),
6453 ),
6452 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6454 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6453 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6455 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6454 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6456 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6455 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6457 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6456 (b'', b'print-url', None, _(b'start and print only the URL')),
6458 (b'', b'print-url', None, _(b'start and print only the URL')),
6457 ]
6459 ]
6458 + subrepoopts,
6460 + subrepoopts,
6459 _(b'[OPTION]...'),
6461 _(b'[OPTION]...'),
6460 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6462 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6461 helpbasic=True,
6463 helpbasic=True,
6462 optionalrepo=True,
6464 optionalrepo=True,
6463 )
6465 )
6464 def serve(ui, repo, **opts):
6466 def serve(ui, repo, **opts):
6465 """start stand-alone webserver
6467 """start stand-alone webserver
6466
6468
6467 Start a local HTTP repository browser and pull server. You can use
6469 Start a local HTTP repository browser and pull server. You can use
6468 this for ad-hoc sharing and browsing of repositories. It is
6470 this for ad-hoc sharing and browsing of repositories. It is
6469 recommended to use a real web server to serve a repository for
6471 recommended to use a real web server to serve a repository for
6470 longer periods of time.
6472 longer periods of time.
6471
6473
6472 Please note that the server does not implement access control.
6474 Please note that the server does not implement access control.
6473 This means that, by default, anybody can read from the server and
6475 This means that, by default, anybody can read from the server and
6474 nobody can write to it by default. Set the ``web.allow-push``
6476 nobody can write to it by default. Set the ``web.allow-push``
6475 option to ``*`` to allow everybody to push to the server. You
6477 option to ``*`` to allow everybody to push to the server. You
6476 should use a real web server if you need to authenticate users.
6478 should use a real web server if you need to authenticate users.
6477
6479
6478 By default, the server logs accesses to stdout and errors to
6480 By default, the server logs accesses to stdout and errors to
6479 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6481 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6480 files.
6482 files.
6481
6483
6482 To have the server choose a free port number to listen on, specify
6484 To have the server choose a free port number to listen on, specify
6483 a port number of 0; in this case, the server will print the port
6485 a port number of 0; in this case, the server will print the port
6484 number it uses.
6486 number it uses.
6485
6487
6486 Returns 0 on success.
6488 Returns 0 on success.
6487 """
6489 """
6488
6490
6489 opts = pycompat.byteskwargs(opts)
6491 opts = pycompat.byteskwargs(opts)
6490 if opts[b"stdio"] and opts[b"cmdserver"]:
6492 if opts[b"stdio"] and opts[b"cmdserver"]:
6491 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6493 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6492 if opts[b"print_url"] and ui.verbose:
6494 if opts[b"print_url"] and ui.verbose:
6493 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6495 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6494
6496
6495 if opts[b"stdio"]:
6497 if opts[b"stdio"]:
6496 if repo is None:
6498 if repo is None:
6497 raise error.RepoError(
6499 raise error.RepoError(
6498 _(b"there is no Mercurial repository here (.hg not found)")
6500 _(b"there is no Mercurial repository here (.hg not found)")
6499 )
6501 )
6500 s = wireprotoserver.sshserver(ui, repo)
6502 s = wireprotoserver.sshserver(ui, repo)
6501 s.serve_forever()
6503 s.serve_forever()
6502
6504
6503 service = server.createservice(ui, repo, opts)
6505 service = server.createservice(ui, repo, opts)
6504 return server.runservice(opts, initfn=service.init, runfn=service.run)
6506 return server.runservice(opts, initfn=service.init, runfn=service.run)
6505
6507
6506
6508
6507 @command(
6509 @command(
6508 b'shelve',
6510 b'shelve',
6509 [
6511 [
6510 (
6512 (
6511 b'A',
6513 b'A',
6512 b'addremove',
6514 b'addremove',
6513 None,
6515 None,
6514 _(b'mark new/missing files as added/removed before shelving'),
6516 _(b'mark new/missing files as added/removed before shelving'),
6515 ),
6517 ),
6516 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6518 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6517 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6519 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6518 (
6520 (
6519 b'',
6521 b'',
6520 b'date',
6522 b'date',
6521 b'',
6523 b'',
6522 _(b'shelve with the specified commit date'),
6524 _(b'shelve with the specified commit date'),
6523 _(b'DATE'),
6525 _(b'DATE'),
6524 ),
6526 ),
6525 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6527 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6526 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6528 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6527 (
6529 (
6528 b'k',
6530 b'k',
6529 b'keep',
6531 b'keep',
6530 False,
6532 False,
6531 _(b'shelve, but keep changes in the working directory'),
6533 _(b'shelve, but keep changes in the working directory'),
6532 ),
6534 ),
6533 (b'l', b'list', None, _(b'list current shelves')),
6535 (b'l', b'list', None, _(b'list current shelves')),
6534 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6536 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6535 (
6537 (
6536 b'n',
6538 b'n',
6537 b'name',
6539 b'name',
6538 b'',
6540 b'',
6539 _(b'use the given name for the shelved commit'),
6541 _(b'use the given name for the shelved commit'),
6540 _(b'NAME'),
6542 _(b'NAME'),
6541 ),
6543 ),
6542 (
6544 (
6543 b'p',
6545 b'p',
6544 b'patch',
6546 b'patch',
6545 None,
6547 None,
6546 _(
6548 _(
6547 b'output patches for changes (provide the names of the shelved '
6549 b'output patches for changes (provide the names of the shelved '
6548 b'changes as positional arguments)'
6550 b'changes as positional arguments)'
6549 ),
6551 ),
6550 ),
6552 ),
6551 (b'i', b'interactive', None, _(b'interactive mode')),
6553 (b'i', b'interactive', None, _(b'interactive mode')),
6552 (
6554 (
6553 b'',
6555 b'',
6554 b'stat',
6556 b'stat',
6555 None,
6557 None,
6556 _(
6558 _(
6557 b'output diffstat-style summary of changes (provide the names of '
6559 b'output diffstat-style summary of changes (provide the names of '
6558 b'the shelved changes as positional arguments)'
6560 b'the shelved changes as positional arguments)'
6559 ),
6561 ),
6560 ),
6562 ),
6561 ]
6563 ]
6562 + cmdutil.walkopts,
6564 + cmdutil.walkopts,
6563 _(b'hg shelve [OPTION]... [FILE]...'),
6565 _(b'hg shelve [OPTION]... [FILE]...'),
6564 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6566 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6565 )
6567 )
6566 def shelve(ui, repo, *pats, **opts):
6568 def shelve(ui, repo, *pats, **opts):
6567 '''save and set aside changes from the working directory
6569 '''save and set aside changes from the working directory
6568
6570
6569 Shelving takes files that "hg status" reports as not clean, saves
6571 Shelving takes files that "hg status" reports as not clean, saves
6570 the modifications to a bundle (a shelved change), and reverts the
6572 the modifications to a bundle (a shelved change), and reverts the
6571 files so that their state in the working directory becomes clean.
6573 files so that their state in the working directory becomes clean.
6572
6574
6573 To restore these changes to the working directory, using "hg
6575 To restore these changes to the working directory, using "hg
6574 unshelve"; this will work even if you switch to a different
6576 unshelve"; this will work even if you switch to a different
6575 commit.
6577 commit.
6576
6578
6577 When no files are specified, "hg shelve" saves all not-clean
6579 When no files are specified, "hg shelve" saves all not-clean
6578 files. If specific files or directories are named, only changes to
6580 files. If specific files or directories are named, only changes to
6579 those files are shelved.
6581 those files are shelved.
6580
6582
6581 In bare shelve (when no files are specified, without interactive,
6583 In bare shelve (when no files are specified, without interactive,
6582 include and exclude option), shelving remembers information if the
6584 include and exclude option), shelving remembers information if the
6583 working directory was on newly created branch, in other words working
6585 working directory was on newly created branch, in other words working
6584 directory was on different branch than its first parent. In this
6586 directory was on different branch than its first parent. In this
6585 situation unshelving restores branch information to the working directory.
6587 situation unshelving restores branch information to the working directory.
6586
6588
6587 Each shelved change has a name that makes it easier to find later.
6589 Each shelved change has a name that makes it easier to find later.
6588 The name of a shelved change defaults to being based on the active
6590 The name of a shelved change defaults to being based on the active
6589 bookmark, or if there is no active bookmark, the current named
6591 bookmark, or if there is no active bookmark, the current named
6590 branch. To specify a different name, use ``--name``.
6592 branch. To specify a different name, use ``--name``.
6591
6593
6592 To see a list of existing shelved changes, use the ``--list``
6594 To see a list of existing shelved changes, use the ``--list``
6593 option. For each shelved change, this will print its name, age,
6595 option. For each shelved change, this will print its name, age,
6594 and description; use ``--patch`` or ``--stat`` for more details.
6596 and description; use ``--patch`` or ``--stat`` for more details.
6595
6597
6596 To delete specific shelved changes, use ``--delete``. To delete
6598 To delete specific shelved changes, use ``--delete``. To delete
6597 all shelved changes, use ``--cleanup``.
6599 all shelved changes, use ``--cleanup``.
6598 '''
6600 '''
6599 opts = pycompat.byteskwargs(opts)
6601 opts = pycompat.byteskwargs(opts)
6600 allowables = [
6602 allowables = [
6601 (b'addremove', {b'create'}), # 'create' is pseudo action
6603 (b'addremove', {b'create'}), # 'create' is pseudo action
6602 (b'unknown', {b'create'}),
6604 (b'unknown', {b'create'}),
6603 (b'cleanup', {b'cleanup'}),
6605 (b'cleanup', {b'cleanup'}),
6604 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6606 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6605 (b'delete', {b'delete'}),
6607 (b'delete', {b'delete'}),
6606 (b'edit', {b'create'}),
6608 (b'edit', {b'create'}),
6607 (b'keep', {b'create'}),
6609 (b'keep', {b'create'}),
6608 (b'list', {b'list'}),
6610 (b'list', {b'list'}),
6609 (b'message', {b'create'}),
6611 (b'message', {b'create'}),
6610 (b'name', {b'create'}),
6612 (b'name', {b'create'}),
6611 (b'patch', {b'patch', b'list'}),
6613 (b'patch', {b'patch', b'list'}),
6612 (b'stat', {b'stat', b'list'}),
6614 (b'stat', {b'stat', b'list'}),
6613 ]
6615 ]
6614
6616
6615 def checkopt(opt):
6617 def checkopt(opt):
6616 if opts.get(opt):
6618 if opts.get(opt):
6617 for i, allowable in allowables:
6619 for i, allowable in allowables:
6618 if opts[i] and opt not in allowable:
6620 if opts[i] and opt not in allowable:
6619 raise error.Abort(
6621 raise error.Abort(
6620 _(
6622 _(
6621 b"options '--%s' and '--%s' may not be "
6623 b"options '--%s' and '--%s' may not be "
6622 b"used together"
6624 b"used together"
6623 )
6625 )
6624 % (opt, i)
6626 % (opt, i)
6625 )
6627 )
6626 return True
6628 return True
6627
6629
6628 if checkopt(b'cleanup'):
6630 if checkopt(b'cleanup'):
6629 if pats:
6631 if pats:
6630 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6632 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6631 return shelvemod.cleanupcmd(ui, repo)
6633 return shelvemod.cleanupcmd(ui, repo)
6632 elif checkopt(b'delete'):
6634 elif checkopt(b'delete'):
6633 return shelvemod.deletecmd(ui, repo, pats)
6635 return shelvemod.deletecmd(ui, repo, pats)
6634 elif checkopt(b'list'):
6636 elif checkopt(b'list'):
6635 return shelvemod.listcmd(ui, repo, pats, opts)
6637 return shelvemod.listcmd(ui, repo, pats, opts)
6636 elif checkopt(b'patch') or checkopt(b'stat'):
6638 elif checkopt(b'patch') or checkopt(b'stat'):
6637 return shelvemod.patchcmds(ui, repo, pats, opts)
6639 return shelvemod.patchcmds(ui, repo, pats, opts)
6638 else:
6640 else:
6639 return shelvemod.createcmd(ui, repo, pats, opts)
6641 return shelvemod.createcmd(ui, repo, pats, opts)
6640
6642
6641
6643
6642 _NOTTERSE = b'nothing'
6644 _NOTTERSE = b'nothing'
6643
6645
6644
6646
6645 @command(
6647 @command(
6646 b'status|st',
6648 b'status|st',
6647 [
6649 [
6648 (b'A', b'all', None, _(b'show status of all files')),
6650 (b'A', b'all', None, _(b'show status of all files')),
6649 (b'm', b'modified', None, _(b'show only modified files')),
6651 (b'm', b'modified', None, _(b'show only modified files')),
6650 (b'a', b'added', None, _(b'show only added files')),
6652 (b'a', b'added', None, _(b'show only added files')),
6651 (b'r', b'removed', None, _(b'show only removed files')),
6653 (b'r', b'removed', None, _(b'show only removed files')),
6652 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6654 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6653 (b'c', b'clean', None, _(b'show only files without changes')),
6655 (b'c', b'clean', None, _(b'show only files without changes')),
6654 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6656 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6655 (b'i', b'ignored', None, _(b'show only ignored files')),
6657 (b'i', b'ignored', None, _(b'show only ignored files')),
6656 (b'n', b'no-status', None, _(b'hide status prefix')),
6658 (b'n', b'no-status', None, _(b'hide status prefix')),
6657 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6659 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6658 (
6660 (
6659 b'C',
6661 b'C',
6660 b'copies',
6662 b'copies',
6661 None,
6663 None,
6662 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6664 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6663 ),
6665 ),
6664 (
6666 (
6665 b'0',
6667 b'0',
6666 b'print0',
6668 b'print0',
6667 None,
6669 None,
6668 _(b'end filenames with NUL, for use with xargs'),
6670 _(b'end filenames with NUL, for use with xargs'),
6669 ),
6671 ),
6670 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6672 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6671 (
6673 (
6672 b'',
6674 b'',
6673 b'change',
6675 b'change',
6674 b'',
6676 b'',
6675 _(b'list the changed files of a revision'),
6677 _(b'list the changed files of a revision'),
6676 _(b'REV'),
6678 _(b'REV'),
6677 ),
6679 ),
6678 ]
6680 ]
6679 + walkopts
6681 + walkopts
6680 + subrepoopts
6682 + subrepoopts
6681 + formatteropts,
6683 + formatteropts,
6682 _(b'[OPTION]... [FILE]...'),
6684 _(b'[OPTION]... [FILE]...'),
6683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6685 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6684 helpbasic=True,
6686 helpbasic=True,
6685 inferrepo=True,
6687 inferrepo=True,
6686 intents={INTENT_READONLY},
6688 intents={INTENT_READONLY},
6687 )
6689 )
6688 def status(ui, repo, *pats, **opts):
6690 def status(ui, repo, *pats, **opts):
6689 """show changed files in the working directory
6691 """show changed files in the working directory
6690
6692
6691 Show status of files in the repository. If names are given, only
6693 Show status of files in the repository. If names are given, only
6692 files that match are shown. Files that are clean or ignored or
6694 files that match are shown. Files that are clean or ignored or
6693 the source of a copy/move operation, are not listed unless
6695 the source of a copy/move operation, are not listed unless
6694 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6696 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6695 Unless options described with "show only ..." are given, the
6697 Unless options described with "show only ..." are given, the
6696 options -mardu are used.
6698 options -mardu are used.
6697
6699
6698 Option -q/--quiet hides untracked (unknown and ignored) files
6700 Option -q/--quiet hides untracked (unknown and ignored) files
6699 unless explicitly requested with -u/--unknown or -i/--ignored.
6701 unless explicitly requested with -u/--unknown or -i/--ignored.
6700
6702
6701 .. note::
6703 .. note::
6702
6704
6703 :hg:`status` may appear to disagree with diff if permissions have
6705 :hg:`status` may appear to disagree with diff if permissions have
6704 changed or a merge has occurred. The standard diff format does
6706 changed or a merge has occurred. The standard diff format does
6705 not report permission changes and diff only reports changes
6707 not report permission changes and diff only reports changes
6706 relative to one merge parent.
6708 relative to one merge parent.
6707
6709
6708 If one revision is given, it is used as the base revision.
6710 If one revision is given, it is used as the base revision.
6709 If two revisions are given, the differences between them are
6711 If two revisions are given, the differences between them are
6710 shown. The --change option can also be used as a shortcut to list
6712 shown. The --change option can also be used as a shortcut to list
6711 the changed files of a revision from its first parent.
6713 the changed files of a revision from its first parent.
6712
6714
6713 The codes used to show the status of files are::
6715 The codes used to show the status of files are::
6714
6716
6715 M = modified
6717 M = modified
6716 A = added
6718 A = added
6717 R = removed
6719 R = removed
6718 C = clean
6720 C = clean
6719 ! = missing (deleted by non-hg command, but still tracked)
6721 ! = missing (deleted by non-hg command, but still tracked)
6720 ? = not tracked
6722 ? = not tracked
6721 I = ignored
6723 I = ignored
6722 = origin of the previous file (with --copies)
6724 = origin of the previous file (with --copies)
6723
6725
6724 .. container:: verbose
6726 .. container:: verbose
6725
6727
6726 The -t/--terse option abbreviates the output by showing only the directory
6728 The -t/--terse option abbreviates the output by showing only the directory
6727 name if all the files in it share the same status. The option takes an
6729 name if all the files in it share the same status. The option takes an
6728 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6730 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6729 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6731 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6730 for 'ignored' and 'c' for clean.
6732 for 'ignored' and 'c' for clean.
6731
6733
6732 It abbreviates only those statuses which are passed. Note that clean and
6734 It abbreviates only those statuses which are passed. Note that clean and
6733 ignored files are not displayed with '--terse ic' unless the -c/--clean
6735 ignored files are not displayed with '--terse ic' unless the -c/--clean
6734 and -i/--ignored options are also used.
6736 and -i/--ignored options are also used.
6735
6737
6736 The -v/--verbose option shows information when the repository is in an
6738 The -v/--verbose option shows information when the repository is in an
6737 unfinished merge, shelve, rebase state etc. You can have this behavior
6739 unfinished merge, shelve, rebase state etc. You can have this behavior
6738 turned on by default by enabling the ``commands.status.verbose`` option.
6740 turned on by default by enabling the ``commands.status.verbose`` option.
6739
6741
6740 You can skip displaying some of these states by setting
6742 You can skip displaying some of these states by setting
6741 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6743 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6742 'histedit', 'merge', 'rebase', or 'unshelve'.
6744 'histedit', 'merge', 'rebase', or 'unshelve'.
6743
6745
6744 Template:
6746 Template:
6745
6747
6746 The following keywords are supported in addition to the common template
6748 The following keywords are supported in addition to the common template
6747 keywords and functions. See also :hg:`help templates`.
6749 keywords and functions. See also :hg:`help templates`.
6748
6750
6749 :path: String. Repository-absolute path of the file.
6751 :path: String. Repository-absolute path of the file.
6750 :source: String. Repository-absolute path of the file originated from.
6752 :source: String. Repository-absolute path of the file originated from.
6751 Available if ``--copies`` is specified.
6753 Available if ``--copies`` is specified.
6752 :status: String. Character denoting file's status.
6754 :status: String. Character denoting file's status.
6753
6755
6754 Examples:
6756 Examples:
6755
6757
6756 - show changes in the working directory relative to a
6758 - show changes in the working directory relative to a
6757 changeset::
6759 changeset::
6758
6760
6759 hg status --rev 9353
6761 hg status --rev 9353
6760
6762
6761 - show changes in the working directory relative to the
6763 - show changes in the working directory relative to the
6762 current directory (see :hg:`help patterns` for more information)::
6764 current directory (see :hg:`help patterns` for more information)::
6763
6765
6764 hg status re:
6766 hg status re:
6765
6767
6766 - show all changes including copies in an existing changeset::
6768 - show all changes including copies in an existing changeset::
6767
6769
6768 hg status --copies --change 9353
6770 hg status --copies --change 9353
6769
6771
6770 - get a NUL separated list of added files, suitable for xargs::
6772 - get a NUL separated list of added files, suitable for xargs::
6771
6773
6772 hg status -an0
6774 hg status -an0
6773
6775
6774 - show more information about the repository status, abbreviating
6776 - show more information about the repository status, abbreviating
6775 added, removed, modified, deleted, and untracked paths::
6777 added, removed, modified, deleted, and untracked paths::
6776
6778
6777 hg status -v -t mardu
6779 hg status -v -t mardu
6778
6780
6779 Returns 0 on success.
6781 Returns 0 on success.
6780
6782
6781 """
6783 """
6782
6784
6783 opts = pycompat.byteskwargs(opts)
6785 opts = pycompat.byteskwargs(opts)
6784 revs = opts.get(b'rev')
6786 revs = opts.get(b'rev')
6785 change = opts.get(b'change')
6787 change = opts.get(b'change')
6786 terse = opts.get(b'terse')
6788 terse = opts.get(b'terse')
6787 if terse is _NOTTERSE:
6789 if terse is _NOTTERSE:
6788 if revs:
6790 if revs:
6789 terse = b''
6791 terse = b''
6790 else:
6792 else:
6791 terse = ui.config(b'commands', b'status.terse')
6793 terse = ui.config(b'commands', b'status.terse')
6792
6794
6793 if revs and change:
6795 if revs and change:
6794 msg = _(b'cannot specify --rev and --change at the same time')
6796 msg = _(b'cannot specify --rev and --change at the same time')
6795 raise error.Abort(msg)
6797 raise error.Abort(msg)
6796 elif revs and terse:
6798 elif revs and terse:
6797 msg = _(b'cannot use --terse with --rev')
6799 msg = _(b'cannot use --terse with --rev')
6798 raise error.Abort(msg)
6800 raise error.Abort(msg)
6799 elif change:
6801 elif change:
6800 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6802 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6801 ctx2 = scmutil.revsingle(repo, change, None)
6803 ctx2 = scmutil.revsingle(repo, change, None)
6802 ctx1 = ctx2.p1()
6804 ctx1 = ctx2.p1()
6803 else:
6805 else:
6804 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6806 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6805 ctx1, ctx2 = scmutil.revpair(repo, revs)
6807 ctx1, ctx2 = scmutil.revpair(repo, revs)
6806
6808
6807 forcerelativevalue = None
6809 forcerelativevalue = None
6808 if ui.hasconfig(b'commands', b'status.relative'):
6810 if ui.hasconfig(b'commands', b'status.relative'):
6809 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6811 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6810 uipathfn = scmutil.getuipathfn(
6812 uipathfn = scmutil.getuipathfn(
6811 repo,
6813 repo,
6812 legacyrelativevalue=bool(pats),
6814 legacyrelativevalue=bool(pats),
6813 forcerelativevalue=forcerelativevalue,
6815 forcerelativevalue=forcerelativevalue,
6814 )
6816 )
6815
6817
6816 if opts.get(b'print0'):
6818 if opts.get(b'print0'):
6817 end = b'\0'
6819 end = b'\0'
6818 else:
6820 else:
6819 end = b'\n'
6821 end = b'\n'
6820 states = b'modified added removed deleted unknown ignored clean'.split()
6822 states = b'modified added removed deleted unknown ignored clean'.split()
6821 show = [k for k in states if opts.get(k)]
6823 show = [k for k in states if opts.get(k)]
6822 if opts.get(b'all'):
6824 if opts.get(b'all'):
6823 show += ui.quiet and (states[:4] + [b'clean']) or states
6825 show += ui.quiet and (states[:4] + [b'clean']) or states
6824
6826
6825 if not show:
6827 if not show:
6826 if ui.quiet:
6828 if ui.quiet:
6827 show = states[:4]
6829 show = states[:4]
6828 else:
6830 else:
6829 show = states[:5]
6831 show = states[:5]
6830
6832
6831 m = scmutil.match(ctx2, pats, opts)
6833 m = scmutil.match(ctx2, pats, opts)
6832 if terse:
6834 if terse:
6833 # we need to compute clean and unknown to terse
6835 # we need to compute clean and unknown to terse
6834 stat = repo.status(
6836 stat = repo.status(
6835 ctx1.node(),
6837 ctx1.node(),
6836 ctx2.node(),
6838 ctx2.node(),
6837 m,
6839 m,
6838 b'ignored' in show or b'i' in terse,
6840 b'ignored' in show or b'i' in terse,
6839 clean=True,
6841 clean=True,
6840 unknown=True,
6842 unknown=True,
6841 listsubrepos=opts.get(b'subrepos'),
6843 listsubrepos=opts.get(b'subrepos'),
6842 )
6844 )
6843
6845
6844 stat = cmdutil.tersedir(stat, terse)
6846 stat = cmdutil.tersedir(stat, terse)
6845 else:
6847 else:
6846 stat = repo.status(
6848 stat = repo.status(
6847 ctx1.node(),
6849 ctx1.node(),
6848 ctx2.node(),
6850 ctx2.node(),
6849 m,
6851 m,
6850 b'ignored' in show,
6852 b'ignored' in show,
6851 b'clean' in show,
6853 b'clean' in show,
6852 b'unknown' in show,
6854 b'unknown' in show,
6853 opts.get(b'subrepos'),
6855 opts.get(b'subrepos'),
6854 )
6856 )
6855
6857
6856 changestates = zip(
6858 changestates = zip(
6857 states,
6859 states,
6858 pycompat.iterbytestr(b'MAR!?IC'),
6860 pycompat.iterbytestr(b'MAR!?IC'),
6859 [getattr(stat, s.decode('utf8')) for s in states],
6861 [getattr(stat, s.decode('utf8')) for s in states],
6860 )
6862 )
6861
6863
6862 copy = {}
6864 copy = {}
6863 if (
6865 if (
6864 opts.get(b'all')
6866 opts.get(b'all')
6865 or opts.get(b'copies')
6867 or opts.get(b'copies')
6866 or ui.configbool(b'ui', b'statuscopies')
6868 or ui.configbool(b'ui', b'statuscopies')
6867 ) and not opts.get(b'no_status'):
6869 ) and not opts.get(b'no_status'):
6868 copy = copies.pathcopies(ctx1, ctx2, m)
6870 copy = copies.pathcopies(ctx1, ctx2, m)
6869
6871
6870 morestatus = None
6872 morestatus = None
6871 if (
6873 if (
6872 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6874 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6873 ) and not ui.plain():
6875 ) and not ui.plain():
6874 morestatus = cmdutil.readmorestatus(repo)
6876 morestatus = cmdutil.readmorestatus(repo)
6875
6877
6876 ui.pager(b'status')
6878 ui.pager(b'status')
6877 fm = ui.formatter(b'status', opts)
6879 fm = ui.formatter(b'status', opts)
6878 fmt = b'%s' + end
6880 fmt = b'%s' + end
6879 showchar = not opts.get(b'no_status')
6881 showchar = not opts.get(b'no_status')
6880
6882
6881 for state, char, files in changestates:
6883 for state, char, files in changestates:
6882 if state in show:
6884 if state in show:
6883 label = b'status.' + state
6885 label = b'status.' + state
6884 for f in files:
6886 for f in files:
6885 fm.startitem()
6887 fm.startitem()
6886 fm.context(ctx=ctx2)
6888 fm.context(ctx=ctx2)
6887 fm.data(itemtype=b'file', path=f)
6889 fm.data(itemtype=b'file', path=f)
6888 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6890 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6889 fm.plain(fmt % uipathfn(f), label=label)
6891 fm.plain(fmt % uipathfn(f), label=label)
6890 if f in copy:
6892 if f in copy:
6891 fm.data(source=copy[f])
6893 fm.data(source=copy[f])
6892 fm.plain(
6894 fm.plain(
6893 (b' %s' + end) % uipathfn(copy[f]),
6895 (b' %s' + end) % uipathfn(copy[f]),
6894 label=b'status.copied',
6896 label=b'status.copied',
6895 )
6897 )
6896 if morestatus:
6898 if morestatus:
6897 morestatus.formatfile(f, fm)
6899 morestatus.formatfile(f, fm)
6898
6900
6899 if morestatus:
6901 if morestatus:
6900 morestatus.formatfooter(fm)
6902 morestatus.formatfooter(fm)
6901 fm.end()
6903 fm.end()
6902
6904
6903
6905
6904 @command(
6906 @command(
6905 b'summary|sum',
6907 b'summary|sum',
6906 [(b'', b'remote', None, _(b'check for push and pull'))],
6908 [(b'', b'remote', None, _(b'check for push and pull'))],
6907 b'[--remote]',
6909 b'[--remote]',
6908 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6910 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6909 helpbasic=True,
6911 helpbasic=True,
6910 intents={INTENT_READONLY},
6912 intents={INTENT_READONLY},
6911 )
6913 )
6912 def summary(ui, repo, **opts):
6914 def summary(ui, repo, **opts):
6913 """summarize working directory state
6915 """summarize working directory state
6914
6916
6915 This generates a brief summary of the working directory state,
6917 This generates a brief summary of the working directory state,
6916 including parents, branch, commit status, phase and available updates.
6918 including parents, branch, commit status, phase and available updates.
6917
6919
6918 With the --remote option, this will check the default paths for
6920 With the --remote option, this will check the default paths for
6919 incoming and outgoing changes. This can be time-consuming.
6921 incoming and outgoing changes. This can be time-consuming.
6920
6922
6921 Returns 0 on success.
6923 Returns 0 on success.
6922 """
6924 """
6923
6925
6924 opts = pycompat.byteskwargs(opts)
6926 opts = pycompat.byteskwargs(opts)
6925 ui.pager(b'summary')
6927 ui.pager(b'summary')
6926 ctx = repo[None]
6928 ctx = repo[None]
6927 parents = ctx.parents()
6929 parents = ctx.parents()
6928 pnode = parents[0].node()
6930 pnode = parents[0].node()
6929 marks = []
6931 marks = []
6930
6932
6931 try:
6933 try:
6932 ms = mergemod.mergestate.read(repo)
6934 ms = mergemod.mergestate.read(repo)
6933 except error.UnsupportedMergeRecords as e:
6935 except error.UnsupportedMergeRecords as e:
6934 s = b' '.join(e.recordtypes)
6936 s = b' '.join(e.recordtypes)
6935 ui.warn(
6937 ui.warn(
6936 _(b'warning: merge state has unsupported record types: %s\n') % s
6938 _(b'warning: merge state has unsupported record types: %s\n') % s
6937 )
6939 )
6938 unresolved = []
6940 unresolved = []
6939 else:
6941 else:
6940 unresolved = list(ms.unresolved())
6942 unresolved = list(ms.unresolved())
6941
6943
6942 for p in parents:
6944 for p in parents:
6943 # label with log.changeset (instead of log.parent) since this
6945 # label with log.changeset (instead of log.parent) since this
6944 # shows a working directory parent *changeset*:
6946 # shows a working directory parent *changeset*:
6945 # i18n: column positioning for "hg summary"
6947 # i18n: column positioning for "hg summary"
6946 ui.write(
6948 ui.write(
6947 _(b'parent: %d:%s ') % (p.rev(), p),
6949 _(b'parent: %d:%s ') % (p.rev(), p),
6948 label=logcmdutil.changesetlabels(p),
6950 label=logcmdutil.changesetlabels(p),
6949 )
6951 )
6950 ui.write(b' '.join(p.tags()), label=b'log.tag')
6952 ui.write(b' '.join(p.tags()), label=b'log.tag')
6951 if p.bookmarks():
6953 if p.bookmarks():
6952 marks.extend(p.bookmarks())
6954 marks.extend(p.bookmarks())
6953 if p.rev() == -1:
6955 if p.rev() == -1:
6954 if not len(repo):
6956 if not len(repo):
6955 ui.write(_(b' (empty repository)'))
6957 ui.write(_(b' (empty repository)'))
6956 else:
6958 else:
6957 ui.write(_(b' (no revision checked out)'))
6959 ui.write(_(b' (no revision checked out)'))
6958 if p.obsolete():
6960 if p.obsolete():
6959 ui.write(_(b' (obsolete)'))
6961 ui.write(_(b' (obsolete)'))
6960 if p.isunstable():
6962 if p.isunstable():
6961 instabilities = (
6963 instabilities = (
6962 ui.label(instability, b'trouble.%s' % instability)
6964 ui.label(instability, b'trouble.%s' % instability)
6963 for instability in p.instabilities()
6965 for instability in p.instabilities()
6964 )
6966 )
6965 ui.write(b' (' + b', '.join(instabilities) + b')')
6967 ui.write(b' (' + b', '.join(instabilities) + b')')
6966 ui.write(b'\n')
6968 ui.write(b'\n')
6967 if p.description():
6969 if p.description():
6968 ui.status(
6970 ui.status(
6969 b' ' + p.description().splitlines()[0].strip() + b'\n',
6971 b' ' + p.description().splitlines()[0].strip() + b'\n',
6970 label=b'log.summary',
6972 label=b'log.summary',
6971 )
6973 )
6972
6974
6973 branch = ctx.branch()
6975 branch = ctx.branch()
6974 bheads = repo.branchheads(branch)
6976 bheads = repo.branchheads(branch)
6975 # i18n: column positioning for "hg summary"
6977 # i18n: column positioning for "hg summary"
6976 m = _(b'branch: %s\n') % branch
6978 m = _(b'branch: %s\n') % branch
6977 if branch != b'default':
6979 if branch != b'default':
6978 ui.write(m, label=b'log.branch')
6980 ui.write(m, label=b'log.branch')
6979 else:
6981 else:
6980 ui.status(m, label=b'log.branch')
6982 ui.status(m, label=b'log.branch')
6981
6983
6982 if marks:
6984 if marks:
6983 active = repo._activebookmark
6985 active = repo._activebookmark
6984 # i18n: column positioning for "hg summary"
6986 # i18n: column positioning for "hg summary"
6985 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6987 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6986 if active is not None:
6988 if active is not None:
6987 if active in marks:
6989 if active in marks:
6988 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6990 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6989 marks.remove(active)
6991 marks.remove(active)
6990 else:
6992 else:
6991 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6993 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6992 for m in marks:
6994 for m in marks:
6993 ui.write(b' ' + m, label=b'log.bookmark')
6995 ui.write(b' ' + m, label=b'log.bookmark')
6994 ui.write(b'\n', label=b'log.bookmark')
6996 ui.write(b'\n', label=b'log.bookmark')
6995
6997
6996 status = repo.status(unknown=True)
6998 status = repo.status(unknown=True)
6997
6999
6998 c = repo.dirstate.copies()
7000 c = repo.dirstate.copies()
6999 copied, renamed = [], []
7001 copied, renamed = [], []
7000 for d, s in pycompat.iteritems(c):
7002 for d, s in pycompat.iteritems(c):
7001 if s in status.removed:
7003 if s in status.removed:
7002 status.removed.remove(s)
7004 status.removed.remove(s)
7003 renamed.append(d)
7005 renamed.append(d)
7004 else:
7006 else:
7005 copied.append(d)
7007 copied.append(d)
7006 if d in status.added:
7008 if d in status.added:
7007 status.added.remove(d)
7009 status.added.remove(d)
7008
7010
7009 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7011 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7010
7012
7011 labels = [
7013 labels = [
7012 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7014 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7013 (ui.label(_(b'%d added'), b'status.added'), status.added),
7015 (ui.label(_(b'%d added'), b'status.added'), status.added),
7014 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7016 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7015 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7017 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7016 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7018 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7017 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7019 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7018 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7020 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7019 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7021 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7020 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7022 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7021 ]
7023 ]
7022 t = []
7024 t = []
7023 for l, s in labels:
7025 for l, s in labels:
7024 if s:
7026 if s:
7025 t.append(l % len(s))
7027 t.append(l % len(s))
7026
7028
7027 t = b', '.join(t)
7029 t = b', '.join(t)
7028 cleanworkdir = False
7030 cleanworkdir = False
7029
7031
7030 if repo.vfs.exists(b'graftstate'):
7032 if repo.vfs.exists(b'graftstate'):
7031 t += _(b' (graft in progress)')
7033 t += _(b' (graft in progress)')
7032 if repo.vfs.exists(b'updatestate'):
7034 if repo.vfs.exists(b'updatestate'):
7033 t += _(b' (interrupted update)')
7035 t += _(b' (interrupted update)')
7034 elif len(parents) > 1:
7036 elif len(parents) > 1:
7035 t += _(b' (merge)')
7037 t += _(b' (merge)')
7036 elif branch != parents[0].branch():
7038 elif branch != parents[0].branch():
7037 t += _(b' (new branch)')
7039 t += _(b' (new branch)')
7038 elif parents[0].closesbranch() and pnode in repo.branchheads(
7040 elif parents[0].closesbranch() and pnode in repo.branchheads(
7039 branch, closed=True
7041 branch, closed=True
7040 ):
7042 ):
7041 t += _(b' (head closed)')
7043 t += _(b' (head closed)')
7042 elif not (
7044 elif not (
7043 status.modified
7045 status.modified
7044 or status.added
7046 or status.added
7045 or status.removed
7047 or status.removed
7046 or renamed
7048 or renamed
7047 or copied
7049 or copied
7048 or subs
7050 or subs
7049 ):
7051 ):
7050 t += _(b' (clean)')
7052 t += _(b' (clean)')
7051 cleanworkdir = True
7053 cleanworkdir = True
7052 elif pnode not in bheads:
7054 elif pnode not in bheads:
7053 t += _(b' (new branch head)')
7055 t += _(b' (new branch head)')
7054
7056
7055 if parents:
7057 if parents:
7056 pendingphase = max(p.phase() for p in parents)
7058 pendingphase = max(p.phase() for p in parents)
7057 else:
7059 else:
7058 pendingphase = phases.public
7060 pendingphase = phases.public
7059
7061
7060 if pendingphase > phases.newcommitphase(ui):
7062 if pendingphase > phases.newcommitphase(ui):
7061 t += b' (%s)' % phases.phasenames[pendingphase]
7063 t += b' (%s)' % phases.phasenames[pendingphase]
7062
7064
7063 if cleanworkdir:
7065 if cleanworkdir:
7064 # i18n: column positioning for "hg summary"
7066 # i18n: column positioning for "hg summary"
7065 ui.status(_(b'commit: %s\n') % t.strip())
7067 ui.status(_(b'commit: %s\n') % t.strip())
7066 else:
7068 else:
7067 # i18n: column positioning for "hg summary"
7069 # i18n: column positioning for "hg summary"
7068 ui.write(_(b'commit: %s\n') % t.strip())
7070 ui.write(_(b'commit: %s\n') % t.strip())
7069
7071
7070 # all ancestors of branch heads - all ancestors of parent = new csets
7072 # all ancestors of branch heads - all ancestors of parent = new csets
7071 new = len(
7073 new = len(
7072 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7074 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7073 )
7075 )
7074
7076
7075 if new == 0:
7077 if new == 0:
7076 # i18n: column positioning for "hg summary"
7078 # i18n: column positioning for "hg summary"
7077 ui.status(_(b'update: (current)\n'))
7079 ui.status(_(b'update: (current)\n'))
7078 elif pnode not in bheads:
7080 elif pnode not in bheads:
7079 # i18n: column positioning for "hg summary"
7081 # i18n: column positioning for "hg summary"
7080 ui.write(_(b'update: %d new changesets (update)\n') % new)
7082 ui.write(_(b'update: %d new changesets (update)\n') % new)
7081 else:
7083 else:
7082 # i18n: column positioning for "hg summary"
7084 # i18n: column positioning for "hg summary"
7083 ui.write(
7085 ui.write(
7084 _(b'update: %d new changesets, %d branch heads (merge)\n')
7086 _(b'update: %d new changesets, %d branch heads (merge)\n')
7085 % (new, len(bheads))
7087 % (new, len(bheads))
7086 )
7088 )
7087
7089
7088 t = []
7090 t = []
7089 draft = len(repo.revs(b'draft()'))
7091 draft = len(repo.revs(b'draft()'))
7090 if draft:
7092 if draft:
7091 t.append(_(b'%d draft') % draft)
7093 t.append(_(b'%d draft') % draft)
7092 secret = len(repo.revs(b'secret()'))
7094 secret = len(repo.revs(b'secret()'))
7093 if secret:
7095 if secret:
7094 t.append(_(b'%d secret') % secret)
7096 t.append(_(b'%d secret') % secret)
7095
7097
7096 if draft or secret:
7098 if draft or secret:
7097 ui.status(_(b'phases: %s\n') % b', '.join(t))
7099 ui.status(_(b'phases: %s\n') % b', '.join(t))
7098
7100
7099 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7101 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7100 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7102 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7101 numtrouble = len(repo.revs(trouble + b"()"))
7103 numtrouble = len(repo.revs(trouble + b"()"))
7102 # We write all the possibilities to ease translation
7104 # We write all the possibilities to ease translation
7103 troublemsg = {
7105 troublemsg = {
7104 b"orphan": _(b"orphan: %d changesets"),
7106 b"orphan": _(b"orphan: %d changesets"),
7105 b"contentdivergent": _(b"content-divergent: %d changesets"),
7107 b"contentdivergent": _(b"content-divergent: %d changesets"),
7106 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7108 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7107 }
7109 }
7108 if numtrouble > 0:
7110 if numtrouble > 0:
7109 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7111 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7110
7112
7111 cmdutil.summaryhooks(ui, repo)
7113 cmdutil.summaryhooks(ui, repo)
7112
7114
7113 if opts.get(b'remote'):
7115 if opts.get(b'remote'):
7114 needsincoming, needsoutgoing = True, True
7116 needsincoming, needsoutgoing = True, True
7115 else:
7117 else:
7116 needsincoming, needsoutgoing = False, False
7118 needsincoming, needsoutgoing = False, False
7117 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7119 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7118 if i:
7120 if i:
7119 needsincoming = True
7121 needsincoming = True
7120 if o:
7122 if o:
7121 needsoutgoing = True
7123 needsoutgoing = True
7122 if not needsincoming and not needsoutgoing:
7124 if not needsincoming and not needsoutgoing:
7123 return
7125 return
7124
7126
7125 def getincoming():
7127 def getincoming():
7126 source, branches = hg.parseurl(ui.expandpath(b'default'))
7128 source, branches = hg.parseurl(ui.expandpath(b'default'))
7127 sbranch = branches[0]
7129 sbranch = branches[0]
7128 try:
7130 try:
7129 other = hg.peer(repo, {}, source)
7131 other = hg.peer(repo, {}, source)
7130 except error.RepoError:
7132 except error.RepoError:
7131 if opts.get(b'remote'):
7133 if opts.get(b'remote'):
7132 raise
7134 raise
7133 return source, sbranch, None, None, None
7135 return source, sbranch, None, None, None
7134 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7136 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7135 if revs:
7137 if revs:
7136 revs = [other.lookup(rev) for rev in revs]
7138 revs = [other.lookup(rev) for rev in revs]
7137 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7139 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7138 repo.ui.pushbuffer()
7140 repo.ui.pushbuffer()
7139 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7141 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7140 repo.ui.popbuffer()
7142 repo.ui.popbuffer()
7141 return source, sbranch, other, commoninc, commoninc[1]
7143 return source, sbranch, other, commoninc, commoninc[1]
7142
7144
7143 if needsincoming:
7145 if needsincoming:
7144 source, sbranch, sother, commoninc, incoming = getincoming()
7146 source, sbranch, sother, commoninc, incoming = getincoming()
7145 else:
7147 else:
7146 source = sbranch = sother = commoninc = incoming = None
7148 source = sbranch = sother = commoninc = incoming = None
7147
7149
7148 def getoutgoing():
7150 def getoutgoing():
7149 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7151 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7150 dbranch = branches[0]
7152 dbranch = branches[0]
7151 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7153 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7152 if source != dest:
7154 if source != dest:
7153 try:
7155 try:
7154 dother = hg.peer(repo, {}, dest)
7156 dother = hg.peer(repo, {}, dest)
7155 except error.RepoError:
7157 except error.RepoError:
7156 if opts.get(b'remote'):
7158 if opts.get(b'remote'):
7157 raise
7159 raise
7158 return dest, dbranch, None, None
7160 return dest, dbranch, None, None
7159 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7161 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7160 elif sother is None:
7162 elif sother is None:
7161 # there is no explicit destination peer, but source one is invalid
7163 # there is no explicit destination peer, but source one is invalid
7162 return dest, dbranch, None, None
7164 return dest, dbranch, None, None
7163 else:
7165 else:
7164 dother = sother
7166 dother = sother
7165 if source != dest or (sbranch is not None and sbranch != dbranch):
7167 if source != dest or (sbranch is not None and sbranch != dbranch):
7166 common = None
7168 common = None
7167 else:
7169 else:
7168 common = commoninc
7170 common = commoninc
7169 if revs:
7171 if revs:
7170 revs = [repo.lookup(rev) for rev in revs]
7172 revs = [repo.lookup(rev) for rev in revs]
7171 repo.ui.pushbuffer()
7173 repo.ui.pushbuffer()
7172 outgoing = discovery.findcommonoutgoing(
7174 outgoing = discovery.findcommonoutgoing(
7173 repo, dother, onlyheads=revs, commoninc=common
7175 repo, dother, onlyheads=revs, commoninc=common
7174 )
7176 )
7175 repo.ui.popbuffer()
7177 repo.ui.popbuffer()
7176 return dest, dbranch, dother, outgoing
7178 return dest, dbranch, dother, outgoing
7177
7179
7178 if needsoutgoing:
7180 if needsoutgoing:
7179 dest, dbranch, dother, outgoing = getoutgoing()
7181 dest, dbranch, dother, outgoing = getoutgoing()
7180 else:
7182 else:
7181 dest = dbranch = dother = outgoing = None
7183 dest = dbranch = dother = outgoing = None
7182
7184
7183 if opts.get(b'remote'):
7185 if opts.get(b'remote'):
7184 t = []
7186 t = []
7185 if incoming:
7187 if incoming:
7186 t.append(_(b'1 or more incoming'))
7188 t.append(_(b'1 or more incoming'))
7187 o = outgoing.missing
7189 o = outgoing.missing
7188 if o:
7190 if o:
7189 t.append(_(b'%d outgoing') % len(o))
7191 t.append(_(b'%d outgoing') % len(o))
7190 other = dother or sother
7192 other = dother or sother
7191 if b'bookmarks' in other.listkeys(b'namespaces'):
7193 if b'bookmarks' in other.listkeys(b'namespaces'):
7192 counts = bookmarks.summary(repo, other)
7194 counts = bookmarks.summary(repo, other)
7193 if counts[0] > 0:
7195 if counts[0] > 0:
7194 t.append(_(b'%d incoming bookmarks') % counts[0])
7196 t.append(_(b'%d incoming bookmarks') % counts[0])
7195 if counts[1] > 0:
7197 if counts[1] > 0:
7196 t.append(_(b'%d outgoing bookmarks') % counts[1])
7198 t.append(_(b'%d outgoing bookmarks') % counts[1])
7197
7199
7198 if t:
7200 if t:
7199 # i18n: column positioning for "hg summary"
7201 # i18n: column positioning for "hg summary"
7200 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7202 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7201 else:
7203 else:
7202 # i18n: column positioning for "hg summary"
7204 # i18n: column positioning for "hg summary"
7203 ui.status(_(b'remote: (synced)\n'))
7205 ui.status(_(b'remote: (synced)\n'))
7204
7206
7205 cmdutil.summaryremotehooks(
7207 cmdutil.summaryremotehooks(
7206 ui,
7208 ui,
7207 repo,
7209 repo,
7208 opts,
7210 opts,
7209 (
7211 (
7210 (source, sbranch, sother, commoninc),
7212 (source, sbranch, sother, commoninc),
7211 (dest, dbranch, dother, outgoing),
7213 (dest, dbranch, dother, outgoing),
7212 ),
7214 ),
7213 )
7215 )
7214
7216
7215
7217
7216 @command(
7218 @command(
7217 b'tag',
7219 b'tag',
7218 [
7220 [
7219 (b'f', b'force', None, _(b'force tag')),
7221 (b'f', b'force', None, _(b'force tag')),
7220 (b'l', b'local', None, _(b'make the tag local')),
7222 (b'l', b'local', None, _(b'make the tag local')),
7221 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7223 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7222 (b'', b'remove', None, _(b'remove a tag')),
7224 (b'', b'remove', None, _(b'remove a tag')),
7223 # -l/--local is already there, commitopts cannot be used
7225 # -l/--local is already there, commitopts cannot be used
7224 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7226 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7225 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7227 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7226 ]
7228 ]
7227 + commitopts2,
7229 + commitopts2,
7228 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7230 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7229 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7231 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7230 )
7232 )
7231 def tag(ui, repo, name1, *names, **opts):
7233 def tag(ui, repo, name1, *names, **opts):
7232 """add one or more tags for the current or given revision
7234 """add one or more tags for the current or given revision
7233
7235
7234 Name a particular revision using <name>.
7236 Name a particular revision using <name>.
7235
7237
7236 Tags are used to name particular revisions of the repository and are
7238 Tags are used to name particular revisions of the repository and are
7237 very useful to compare different revisions, to go back to significant
7239 very useful to compare different revisions, to go back to significant
7238 earlier versions or to mark branch points as releases, etc. Changing
7240 earlier versions or to mark branch points as releases, etc. Changing
7239 an existing tag is normally disallowed; use -f/--force to override.
7241 an existing tag is normally disallowed; use -f/--force to override.
7240
7242
7241 If no revision is given, the parent of the working directory is
7243 If no revision is given, the parent of the working directory is
7242 used.
7244 used.
7243
7245
7244 To facilitate version control, distribution, and merging of tags,
7246 To facilitate version control, distribution, and merging of tags,
7245 they are stored as a file named ".hgtags" which is managed similarly
7247 they are stored as a file named ".hgtags" which is managed similarly
7246 to other project files and can be hand-edited if necessary. This
7248 to other project files and can be hand-edited if necessary. This
7247 also means that tagging creates a new commit. The file
7249 also means that tagging creates a new commit. The file
7248 ".hg/localtags" is used for local tags (not shared among
7250 ".hg/localtags" is used for local tags (not shared among
7249 repositories).
7251 repositories).
7250
7252
7251 Tag commits are usually made at the head of a branch. If the parent
7253 Tag commits are usually made at the head of a branch. If the parent
7252 of the working directory is not a branch head, :hg:`tag` aborts; use
7254 of the working directory is not a branch head, :hg:`tag` aborts; use
7253 -f/--force to force the tag commit to be based on a non-head
7255 -f/--force to force the tag commit to be based on a non-head
7254 changeset.
7256 changeset.
7255
7257
7256 See :hg:`help dates` for a list of formats valid for -d/--date.
7258 See :hg:`help dates` for a list of formats valid for -d/--date.
7257
7259
7258 Since tag names have priority over branch names during revision
7260 Since tag names have priority over branch names during revision
7259 lookup, using an existing branch name as a tag name is discouraged.
7261 lookup, using an existing branch name as a tag name is discouraged.
7260
7262
7261 Returns 0 on success.
7263 Returns 0 on success.
7262 """
7264 """
7263 opts = pycompat.byteskwargs(opts)
7265 opts = pycompat.byteskwargs(opts)
7264 with repo.wlock(), repo.lock():
7266 with repo.wlock(), repo.lock():
7265 rev_ = b"."
7267 rev_ = b"."
7266 names = [t.strip() for t in (name1,) + names]
7268 names = [t.strip() for t in (name1,) + names]
7267 if len(names) != len(set(names)):
7269 if len(names) != len(set(names)):
7268 raise error.Abort(_(b'tag names must be unique'))
7270 raise error.Abort(_(b'tag names must be unique'))
7269 for n in names:
7271 for n in names:
7270 scmutil.checknewlabel(repo, n, b'tag')
7272 scmutil.checknewlabel(repo, n, b'tag')
7271 if not n:
7273 if not n:
7272 raise error.Abort(
7274 raise error.Abort(
7273 _(b'tag names cannot consist entirely of whitespace')
7275 _(b'tag names cannot consist entirely of whitespace')
7274 )
7276 )
7275 if opts.get(b'rev') and opts.get(b'remove'):
7277 if opts.get(b'rev') and opts.get(b'remove'):
7276 raise error.Abort(_(b"--rev and --remove are incompatible"))
7278 raise error.Abort(_(b"--rev and --remove are incompatible"))
7277 if opts.get(b'rev'):
7279 if opts.get(b'rev'):
7278 rev_ = opts[b'rev']
7280 rev_ = opts[b'rev']
7279 message = opts.get(b'message')
7281 message = opts.get(b'message')
7280 if opts.get(b'remove'):
7282 if opts.get(b'remove'):
7281 if opts.get(b'local'):
7283 if opts.get(b'local'):
7282 expectedtype = b'local'
7284 expectedtype = b'local'
7283 else:
7285 else:
7284 expectedtype = b'global'
7286 expectedtype = b'global'
7285
7287
7286 for n in names:
7288 for n in names:
7287 if repo.tagtype(n) == b'global':
7289 if repo.tagtype(n) == b'global':
7288 alltags = tagsmod.findglobaltags(ui, repo)
7290 alltags = tagsmod.findglobaltags(ui, repo)
7289 if alltags[n][0] == nullid:
7291 if alltags[n][0] == nullid:
7290 raise error.Abort(_(b"tag '%s' is already removed") % n)
7292 raise error.Abort(_(b"tag '%s' is already removed") % n)
7291 if not repo.tagtype(n):
7293 if not repo.tagtype(n):
7292 raise error.Abort(_(b"tag '%s' does not exist") % n)
7294 raise error.Abort(_(b"tag '%s' does not exist") % n)
7293 if repo.tagtype(n) != expectedtype:
7295 if repo.tagtype(n) != expectedtype:
7294 if expectedtype == b'global':
7296 if expectedtype == b'global':
7295 raise error.Abort(
7297 raise error.Abort(
7296 _(b"tag '%s' is not a global tag") % n
7298 _(b"tag '%s' is not a global tag") % n
7297 )
7299 )
7298 else:
7300 else:
7299 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7301 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7300 rev_ = b'null'
7302 rev_ = b'null'
7301 if not message:
7303 if not message:
7302 # we don't translate commit messages
7304 # we don't translate commit messages
7303 message = b'Removed tag %s' % b', '.join(names)
7305 message = b'Removed tag %s' % b', '.join(names)
7304 elif not opts.get(b'force'):
7306 elif not opts.get(b'force'):
7305 for n in names:
7307 for n in names:
7306 if n in repo.tags():
7308 if n in repo.tags():
7307 raise error.Abort(
7309 raise error.Abort(
7308 _(b"tag '%s' already exists (use -f to force)") % n
7310 _(b"tag '%s' already exists (use -f to force)") % n
7309 )
7311 )
7310 if not opts.get(b'local'):
7312 if not opts.get(b'local'):
7311 p1, p2 = repo.dirstate.parents()
7313 p1, p2 = repo.dirstate.parents()
7312 if p2 != nullid:
7314 if p2 != nullid:
7313 raise error.Abort(_(b'uncommitted merge'))
7315 raise error.Abort(_(b'uncommitted merge'))
7314 bheads = repo.branchheads()
7316 bheads = repo.branchheads()
7315 if not opts.get(b'force') and bheads and p1 not in bheads:
7317 if not opts.get(b'force') and bheads and p1 not in bheads:
7316 raise error.Abort(
7318 raise error.Abort(
7317 _(
7319 _(
7318 b'working directory is not at a branch head '
7320 b'working directory is not at a branch head '
7319 b'(use -f to force)'
7321 b'(use -f to force)'
7320 )
7322 )
7321 )
7323 )
7322 node = scmutil.revsingle(repo, rev_).node()
7324 node = scmutil.revsingle(repo, rev_).node()
7323
7325
7324 if not message:
7326 if not message:
7325 # we don't translate commit messages
7327 # we don't translate commit messages
7326 message = b'Added tag %s for changeset %s' % (
7328 message = b'Added tag %s for changeset %s' % (
7327 b', '.join(names),
7329 b', '.join(names),
7328 short(node),
7330 short(node),
7329 )
7331 )
7330
7332
7331 date = opts.get(b'date')
7333 date = opts.get(b'date')
7332 if date:
7334 if date:
7333 date = dateutil.parsedate(date)
7335 date = dateutil.parsedate(date)
7334
7336
7335 if opts.get(b'remove'):
7337 if opts.get(b'remove'):
7336 editform = b'tag.remove'
7338 editform = b'tag.remove'
7337 else:
7339 else:
7338 editform = b'tag.add'
7340 editform = b'tag.add'
7339 editor = cmdutil.getcommiteditor(
7341 editor = cmdutil.getcommiteditor(
7340 editform=editform, **pycompat.strkwargs(opts)
7342 editform=editform, **pycompat.strkwargs(opts)
7341 )
7343 )
7342
7344
7343 # don't allow tagging the null rev
7345 # don't allow tagging the null rev
7344 if (
7346 if (
7345 not opts.get(b'remove')
7347 not opts.get(b'remove')
7346 and scmutil.revsingle(repo, rev_).rev() == nullrev
7348 and scmutil.revsingle(repo, rev_).rev() == nullrev
7347 ):
7349 ):
7348 raise error.Abort(_(b"cannot tag null revision"))
7350 raise error.Abort(_(b"cannot tag null revision"))
7349
7351
7350 tagsmod.tag(
7352 tagsmod.tag(
7351 repo,
7353 repo,
7352 names,
7354 names,
7353 node,
7355 node,
7354 message,
7356 message,
7355 opts.get(b'local'),
7357 opts.get(b'local'),
7356 opts.get(b'user'),
7358 opts.get(b'user'),
7357 date,
7359 date,
7358 editor=editor,
7360 editor=editor,
7359 )
7361 )
7360
7362
7361
7363
7362 @command(
7364 @command(
7363 b'tags',
7365 b'tags',
7364 formatteropts,
7366 formatteropts,
7365 b'',
7367 b'',
7366 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7368 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7367 intents={INTENT_READONLY},
7369 intents={INTENT_READONLY},
7368 )
7370 )
7369 def tags(ui, repo, **opts):
7371 def tags(ui, repo, **opts):
7370 """list repository tags
7372 """list repository tags
7371
7373
7372 This lists both regular and local tags. When the -v/--verbose
7374 This lists both regular and local tags. When the -v/--verbose
7373 switch is used, a third column "local" is printed for local tags.
7375 switch is used, a third column "local" is printed for local tags.
7374 When the -q/--quiet switch is used, only the tag name is printed.
7376 When the -q/--quiet switch is used, only the tag name is printed.
7375
7377
7376 .. container:: verbose
7378 .. container:: verbose
7377
7379
7378 Template:
7380 Template:
7379
7381
7380 The following keywords are supported in addition to the common template
7382 The following keywords are supported in addition to the common template
7381 keywords and functions such as ``{tag}``. See also
7383 keywords and functions such as ``{tag}``. See also
7382 :hg:`help templates`.
7384 :hg:`help templates`.
7383
7385
7384 :type: String. ``local`` for local tags.
7386 :type: String. ``local`` for local tags.
7385
7387
7386 Returns 0 on success.
7388 Returns 0 on success.
7387 """
7389 """
7388
7390
7389 opts = pycompat.byteskwargs(opts)
7391 opts = pycompat.byteskwargs(opts)
7390 ui.pager(b'tags')
7392 ui.pager(b'tags')
7391 fm = ui.formatter(b'tags', opts)
7393 fm = ui.formatter(b'tags', opts)
7392 hexfunc = fm.hexfunc
7394 hexfunc = fm.hexfunc
7393
7395
7394 for t, n in reversed(repo.tagslist()):
7396 for t, n in reversed(repo.tagslist()):
7395 hn = hexfunc(n)
7397 hn = hexfunc(n)
7396 label = b'tags.normal'
7398 label = b'tags.normal'
7397 tagtype = b''
7399 tagtype = b''
7398 if repo.tagtype(t) == b'local':
7400 if repo.tagtype(t) == b'local':
7399 label = b'tags.local'
7401 label = b'tags.local'
7400 tagtype = b'local'
7402 tagtype = b'local'
7401
7403
7402 fm.startitem()
7404 fm.startitem()
7403 fm.context(repo=repo)
7405 fm.context(repo=repo)
7404 fm.write(b'tag', b'%s', t, label=label)
7406 fm.write(b'tag', b'%s', t, label=label)
7405 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7407 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7406 fm.condwrite(
7408 fm.condwrite(
7407 not ui.quiet,
7409 not ui.quiet,
7408 b'rev node',
7410 b'rev node',
7409 fmt,
7411 fmt,
7410 repo.changelog.rev(n),
7412 repo.changelog.rev(n),
7411 hn,
7413 hn,
7412 label=label,
7414 label=label,
7413 )
7415 )
7414 fm.condwrite(
7416 fm.condwrite(
7415 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7417 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7416 )
7418 )
7417 fm.plain(b'\n')
7419 fm.plain(b'\n')
7418 fm.end()
7420 fm.end()
7419
7421
7420
7422
7421 @command(
7423 @command(
7422 b'tip',
7424 b'tip',
7423 [
7425 [
7424 (b'p', b'patch', None, _(b'show patch')),
7426 (b'p', b'patch', None, _(b'show patch')),
7425 (b'g', b'git', None, _(b'use git extended diff format')),
7427 (b'g', b'git', None, _(b'use git extended diff format')),
7426 ]
7428 ]
7427 + templateopts,
7429 + templateopts,
7428 _(b'[-p] [-g]'),
7430 _(b'[-p] [-g]'),
7429 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7431 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7430 )
7432 )
7431 def tip(ui, repo, **opts):
7433 def tip(ui, repo, **opts):
7432 """show the tip revision (DEPRECATED)
7434 """show the tip revision (DEPRECATED)
7433
7435
7434 The tip revision (usually just called the tip) is the changeset
7436 The tip revision (usually just called the tip) is the changeset
7435 most recently added to the repository (and therefore the most
7437 most recently added to the repository (and therefore the most
7436 recently changed head).
7438 recently changed head).
7437
7439
7438 If you have just made a commit, that commit will be the tip. If
7440 If you have just made a commit, that commit will be the tip. If
7439 you have just pulled changes from another repository, the tip of
7441 you have just pulled changes from another repository, the tip of
7440 that repository becomes the current tip. The "tip" tag is special
7442 that repository becomes the current tip. The "tip" tag is special
7441 and cannot be renamed or assigned to a different changeset.
7443 and cannot be renamed or assigned to a different changeset.
7442
7444
7443 This command is deprecated, please use :hg:`heads` instead.
7445 This command is deprecated, please use :hg:`heads` instead.
7444
7446
7445 Returns 0 on success.
7447 Returns 0 on success.
7446 """
7448 """
7447 opts = pycompat.byteskwargs(opts)
7449 opts = pycompat.byteskwargs(opts)
7448 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7450 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7449 displayer.show(repo[b'tip'])
7451 displayer.show(repo[b'tip'])
7450 displayer.close()
7452 displayer.close()
7451
7453
7452
7454
7453 @command(
7455 @command(
7454 b'unbundle',
7456 b'unbundle',
7455 [
7457 [
7456 (
7458 (
7457 b'u',
7459 b'u',
7458 b'update',
7460 b'update',
7459 None,
7461 None,
7460 _(b'update to new branch head if changesets were unbundled'),
7462 _(b'update to new branch head if changesets were unbundled'),
7461 )
7463 )
7462 ],
7464 ],
7463 _(b'[-u] FILE...'),
7465 _(b'[-u] FILE...'),
7464 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7466 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7465 )
7467 )
7466 def unbundle(ui, repo, fname1, *fnames, **opts):
7468 def unbundle(ui, repo, fname1, *fnames, **opts):
7467 """apply one or more bundle files
7469 """apply one or more bundle files
7468
7470
7469 Apply one or more bundle files generated by :hg:`bundle`.
7471 Apply one or more bundle files generated by :hg:`bundle`.
7470
7472
7471 Returns 0 on success, 1 if an update has unresolved files.
7473 Returns 0 on success, 1 if an update has unresolved files.
7472 """
7474 """
7473 fnames = (fname1,) + fnames
7475 fnames = (fname1,) + fnames
7474
7476
7475 with repo.lock():
7477 with repo.lock():
7476 for fname in fnames:
7478 for fname in fnames:
7477 f = hg.openpath(ui, fname)
7479 f = hg.openpath(ui, fname)
7478 gen = exchange.readbundle(ui, f, fname)
7480 gen = exchange.readbundle(ui, f, fname)
7479 if isinstance(gen, streamclone.streamcloneapplier):
7481 if isinstance(gen, streamclone.streamcloneapplier):
7480 raise error.Abort(
7482 raise error.Abort(
7481 _(
7483 _(
7482 b'packed bundles cannot be applied with '
7484 b'packed bundles cannot be applied with '
7483 b'"hg unbundle"'
7485 b'"hg unbundle"'
7484 ),
7486 ),
7485 hint=_(b'use "hg debugapplystreamclonebundle"'),
7487 hint=_(b'use "hg debugapplystreamclonebundle"'),
7486 )
7488 )
7487 url = b'bundle:' + fname
7489 url = b'bundle:' + fname
7488 try:
7490 try:
7489 txnname = b'unbundle'
7491 txnname = b'unbundle'
7490 if not isinstance(gen, bundle2.unbundle20):
7492 if not isinstance(gen, bundle2.unbundle20):
7491 txnname = b'unbundle\n%s' % util.hidepassword(url)
7493 txnname = b'unbundle\n%s' % util.hidepassword(url)
7492 with repo.transaction(txnname) as tr:
7494 with repo.transaction(txnname) as tr:
7493 op = bundle2.applybundle(
7495 op = bundle2.applybundle(
7494 repo, gen, tr, source=b'unbundle', url=url
7496 repo, gen, tr, source=b'unbundle', url=url
7495 )
7497 )
7496 except error.BundleUnknownFeatureError as exc:
7498 except error.BundleUnknownFeatureError as exc:
7497 raise error.Abort(
7499 raise error.Abort(
7498 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7500 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7499 hint=_(
7501 hint=_(
7500 b"see https://mercurial-scm.org/"
7502 b"see https://mercurial-scm.org/"
7501 b"wiki/BundleFeature for more "
7503 b"wiki/BundleFeature for more "
7502 b"information"
7504 b"information"
7503 ),
7505 ),
7504 )
7506 )
7505 modheads = bundle2.combinechangegroupresults(op)
7507 modheads = bundle2.combinechangegroupresults(op)
7506
7508
7507 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7509 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7508
7510
7509
7511
7510 @command(
7512 @command(
7511 b'unshelve',
7513 b'unshelve',
7512 [
7514 [
7513 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7515 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7514 (
7516 (
7515 b'c',
7517 b'c',
7516 b'continue',
7518 b'continue',
7517 None,
7519 None,
7518 _(b'continue an incomplete unshelve operation'),
7520 _(b'continue an incomplete unshelve operation'),
7519 ),
7521 ),
7520 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7522 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7521 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7523 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7522 (
7524 (
7523 b'n',
7525 b'n',
7524 b'name',
7526 b'name',
7525 b'',
7527 b'',
7526 _(b'restore shelved change with given name'),
7528 _(b'restore shelved change with given name'),
7527 _(b'NAME'),
7529 _(b'NAME'),
7528 ),
7530 ),
7529 (b't', b'tool', b'', _(b'specify merge tool')),
7531 (b't', b'tool', b'', _(b'specify merge tool')),
7530 (
7532 (
7531 b'',
7533 b'',
7532 b'date',
7534 b'date',
7533 b'',
7535 b'',
7534 _(b'set date for temporary commits (DEPRECATED)'),
7536 _(b'set date for temporary commits (DEPRECATED)'),
7535 _(b'DATE'),
7537 _(b'DATE'),
7536 ),
7538 ),
7537 ],
7539 ],
7538 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7540 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7539 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7541 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7540 )
7542 )
7541 def unshelve(ui, repo, *shelved, **opts):
7543 def unshelve(ui, repo, *shelved, **opts):
7542 """restore a shelved change to the working directory
7544 """restore a shelved change to the working directory
7543
7545
7544 This command accepts an optional name of a shelved change to
7546 This command accepts an optional name of a shelved change to
7545 restore. If none is given, the most recent shelved change is used.
7547 restore. If none is given, the most recent shelved change is used.
7546
7548
7547 If a shelved change is applied successfully, the bundle that
7549 If a shelved change is applied successfully, the bundle that
7548 contains the shelved changes is moved to a backup location
7550 contains the shelved changes is moved to a backup location
7549 (.hg/shelve-backup).
7551 (.hg/shelve-backup).
7550
7552
7551 Since you can restore a shelved change on top of an arbitrary
7553 Since you can restore a shelved change on top of an arbitrary
7552 commit, it is possible that unshelving will result in a conflict
7554 commit, it is possible that unshelving will result in a conflict
7553 between your changes and the commits you are unshelving onto. If
7555 between your changes and the commits you are unshelving onto. If
7554 this occurs, you must resolve the conflict, then use
7556 this occurs, you must resolve the conflict, then use
7555 ``--continue`` to complete the unshelve operation. (The bundle
7557 ``--continue`` to complete the unshelve operation. (The bundle
7556 will not be moved until you successfully complete the unshelve.)
7558 will not be moved until you successfully complete the unshelve.)
7557
7559
7558 (Alternatively, you can use ``--abort`` to abandon an unshelve
7560 (Alternatively, you can use ``--abort`` to abandon an unshelve
7559 that causes a conflict. This reverts the unshelved changes, and
7561 that causes a conflict. This reverts the unshelved changes, and
7560 leaves the bundle in place.)
7562 leaves the bundle in place.)
7561
7563
7562 If bare shelved change (without interactive, include and exclude
7564 If bare shelved change (without interactive, include and exclude
7563 option) was done on newly created branch it would restore branch
7565 option) was done on newly created branch it would restore branch
7564 information to the working directory.
7566 information to the working directory.
7565
7567
7566 After a successful unshelve, the shelved changes are stored in a
7568 After a successful unshelve, the shelved changes are stored in a
7567 backup directory. Only the N most recent backups are kept. N
7569 backup directory. Only the N most recent backups are kept. N
7568 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7570 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7569 configuration option.
7571 configuration option.
7570
7572
7571 .. container:: verbose
7573 .. container:: verbose
7572
7574
7573 Timestamp in seconds is used to decide order of backups. More
7575 Timestamp in seconds is used to decide order of backups. More
7574 than ``maxbackups`` backups are kept, if same timestamp
7576 than ``maxbackups`` backups are kept, if same timestamp
7575 prevents from deciding exact order of them, for safety.
7577 prevents from deciding exact order of them, for safety.
7576
7578
7577 Selected changes can be unshelved with ``--interactive`` flag.
7579 Selected changes can be unshelved with ``--interactive`` flag.
7578 The working directory is updated with the selected changes, and
7580 The working directory is updated with the selected changes, and
7579 only the unselected changes remain shelved.
7581 only the unselected changes remain shelved.
7580 Note: The whole shelve is applied to working directory first before
7582 Note: The whole shelve is applied to working directory first before
7581 running interactively. So, this will bring up all the conflicts between
7583 running interactively. So, this will bring up all the conflicts between
7582 working directory and the shelve, irrespective of which changes will be
7584 working directory and the shelve, irrespective of which changes will be
7583 unshelved.
7585 unshelved.
7584 """
7586 """
7585 with repo.wlock():
7587 with repo.wlock():
7586 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7588 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7587
7589
7588
7590
7589 statemod.addunfinished(
7591 statemod.addunfinished(
7590 b'unshelve',
7592 b'unshelve',
7591 fname=b'shelvedstate',
7593 fname=b'shelvedstate',
7592 continueflag=True,
7594 continueflag=True,
7593 abortfunc=shelvemod.hgabortunshelve,
7595 abortfunc=shelvemod.hgabortunshelve,
7594 continuefunc=shelvemod.hgcontinueunshelve,
7596 continuefunc=shelvemod.hgcontinueunshelve,
7595 cmdmsg=_(b'unshelve already in progress'),
7597 cmdmsg=_(b'unshelve already in progress'),
7596 )
7598 )
7597
7599
7598
7600
7599 @command(
7601 @command(
7600 b'update|up|checkout|co',
7602 b'update|up|checkout|co',
7601 [
7603 [
7602 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7604 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7603 (b'c', b'check', None, _(b'require clean working directory')),
7605 (b'c', b'check', None, _(b'require clean working directory')),
7604 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7606 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7605 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7607 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7606 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7608 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7607 ]
7609 ]
7608 + mergetoolopts,
7610 + mergetoolopts,
7609 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7611 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7610 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7612 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7611 helpbasic=True,
7613 helpbasic=True,
7612 )
7614 )
7613 def update(ui, repo, node=None, **opts):
7615 def update(ui, repo, node=None, **opts):
7614 """update working directory (or switch revisions)
7616 """update working directory (or switch revisions)
7615
7617
7616 Update the repository's working directory to the specified
7618 Update the repository's working directory to the specified
7617 changeset. If no changeset is specified, update to the tip of the
7619 changeset. If no changeset is specified, update to the tip of the
7618 current named branch and move the active bookmark (see :hg:`help
7620 current named branch and move the active bookmark (see :hg:`help
7619 bookmarks`).
7621 bookmarks`).
7620
7622
7621 Update sets the working directory's parent revision to the specified
7623 Update sets the working directory's parent revision to the specified
7622 changeset (see :hg:`help parents`).
7624 changeset (see :hg:`help parents`).
7623
7625
7624 If the changeset is not a descendant or ancestor of the working
7626 If the changeset is not a descendant or ancestor of the working
7625 directory's parent and there are uncommitted changes, the update is
7627 directory's parent and there are uncommitted changes, the update is
7626 aborted. With the -c/--check option, the working directory is checked
7628 aborted. With the -c/--check option, the working directory is checked
7627 for uncommitted changes; if none are found, the working directory is
7629 for uncommitted changes; if none are found, the working directory is
7628 updated to the specified changeset.
7630 updated to the specified changeset.
7629
7631
7630 .. container:: verbose
7632 .. container:: verbose
7631
7633
7632 The -C/--clean, -c/--check, and -m/--merge options control what
7634 The -C/--clean, -c/--check, and -m/--merge options control what
7633 happens if the working directory contains uncommitted changes.
7635 happens if the working directory contains uncommitted changes.
7634 At most of one of them can be specified.
7636 At most of one of them can be specified.
7635
7637
7636 1. If no option is specified, and if
7638 1. If no option is specified, and if
7637 the requested changeset is an ancestor or descendant of
7639 the requested changeset is an ancestor or descendant of
7638 the working directory's parent, the uncommitted changes
7640 the working directory's parent, the uncommitted changes
7639 are merged into the requested changeset and the merged
7641 are merged into the requested changeset and the merged
7640 result is left uncommitted. If the requested changeset is
7642 result is left uncommitted. If the requested changeset is
7641 not an ancestor or descendant (that is, it is on another
7643 not an ancestor or descendant (that is, it is on another
7642 branch), the update is aborted and the uncommitted changes
7644 branch), the update is aborted and the uncommitted changes
7643 are preserved.
7645 are preserved.
7644
7646
7645 2. With the -m/--merge option, the update is allowed even if the
7647 2. With the -m/--merge option, the update is allowed even if the
7646 requested changeset is not an ancestor or descendant of
7648 requested changeset is not an ancestor or descendant of
7647 the working directory's parent.
7649 the working directory's parent.
7648
7650
7649 3. With the -c/--check option, the update is aborted and the
7651 3. With the -c/--check option, the update is aborted and the
7650 uncommitted changes are preserved.
7652 uncommitted changes are preserved.
7651
7653
7652 4. With the -C/--clean option, uncommitted changes are discarded and
7654 4. With the -C/--clean option, uncommitted changes are discarded and
7653 the working directory is updated to the requested changeset.
7655 the working directory is updated to the requested changeset.
7654
7656
7655 To cancel an uncommitted merge (and lose your changes), use
7657 To cancel an uncommitted merge (and lose your changes), use
7656 :hg:`merge --abort`.
7658 :hg:`merge --abort`.
7657
7659
7658 Use null as the changeset to remove the working directory (like
7660 Use null as the changeset to remove the working directory (like
7659 :hg:`clone -U`).
7661 :hg:`clone -U`).
7660
7662
7661 If you want to revert just one file to an older revision, use
7663 If you want to revert just one file to an older revision, use
7662 :hg:`revert [-r REV] NAME`.
7664 :hg:`revert [-r REV] NAME`.
7663
7665
7664 See :hg:`help dates` for a list of formats valid for -d/--date.
7666 See :hg:`help dates` for a list of formats valid for -d/--date.
7665
7667
7666 Returns 0 on success, 1 if there are unresolved files.
7668 Returns 0 on success, 1 if there are unresolved files.
7667 """
7669 """
7668 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7670 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7669 rev = opts.get('rev')
7671 rev = opts.get('rev')
7670 date = opts.get('date')
7672 date = opts.get('date')
7671 clean = opts.get('clean')
7673 clean = opts.get('clean')
7672 check = opts.get('check')
7674 check = opts.get('check')
7673 merge = opts.get('merge')
7675 merge = opts.get('merge')
7674 if rev and node:
7676 if rev and node:
7675 raise error.Abort(_(b"please specify just one revision"))
7677 raise error.Abort(_(b"please specify just one revision"))
7676
7678
7677 if ui.configbool(b'commands', b'update.requiredest'):
7679 if ui.configbool(b'commands', b'update.requiredest'):
7678 if not node and not rev and not date:
7680 if not node and not rev and not date:
7679 raise error.Abort(
7681 raise error.Abort(
7680 _(b'you must specify a destination'),
7682 _(b'you must specify a destination'),
7681 hint=_(b'for example: hg update ".::"'),
7683 hint=_(b'for example: hg update ".::"'),
7682 )
7684 )
7683
7685
7684 if rev is None or rev == b'':
7686 if rev is None or rev == b'':
7685 rev = node
7687 rev = node
7686
7688
7687 if date and rev is not None:
7689 if date and rev is not None:
7688 raise error.Abort(_(b"you can't specify a revision and a date"))
7690 raise error.Abort(_(b"you can't specify a revision and a date"))
7689
7691
7690 updatecheck = None
7692 updatecheck = None
7691 if check:
7693 if check:
7692 updatecheck = b'abort'
7694 updatecheck = b'abort'
7693 elif merge:
7695 elif merge:
7694 updatecheck = b'none'
7696 updatecheck = b'none'
7695
7697
7696 with repo.wlock():
7698 with repo.wlock():
7697 cmdutil.clearunfinished(repo)
7699 cmdutil.clearunfinished(repo)
7698 if date:
7700 if date:
7699 rev = cmdutil.finddate(ui, repo, date)
7701 rev = cmdutil.finddate(ui, repo, date)
7700
7702
7701 # if we defined a bookmark, we have to remember the original name
7703 # if we defined a bookmark, we have to remember the original name
7702 brev = rev
7704 brev = rev
7703 if rev:
7705 if rev:
7704 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7706 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7705 ctx = scmutil.revsingle(repo, rev, default=None)
7707 ctx = scmutil.revsingle(repo, rev, default=None)
7706 rev = ctx.rev()
7708 rev = ctx.rev()
7707 hidden = ctx.hidden()
7709 hidden = ctx.hidden()
7708 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7710 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7709 with ui.configoverride(overrides, b'update'):
7711 with ui.configoverride(overrides, b'update'):
7710 ret = hg.updatetotally(
7712 ret = hg.updatetotally(
7711 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7713 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7712 )
7714 )
7713 if hidden:
7715 if hidden:
7714 ctxstr = ctx.hex()[:12]
7716 ctxstr = ctx.hex()[:12]
7715 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7717 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7716
7718
7717 if ctx.obsolete():
7719 if ctx.obsolete():
7718 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7720 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7719 ui.warn(b"(%s)\n" % obsfatemsg)
7721 ui.warn(b"(%s)\n" % obsfatemsg)
7720 return ret
7722 return ret
7721
7723
7722
7724
7723 @command(
7725 @command(
7724 b'verify',
7726 b'verify',
7725 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7727 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7726 helpcategory=command.CATEGORY_MAINTENANCE,
7728 helpcategory=command.CATEGORY_MAINTENANCE,
7727 )
7729 )
7728 def verify(ui, repo, **opts):
7730 def verify(ui, repo, **opts):
7729 """verify the integrity of the repository
7731 """verify the integrity of the repository
7730
7732
7731 Verify the integrity of the current repository.
7733 Verify the integrity of the current repository.
7732
7734
7733 This will perform an extensive check of the repository's
7735 This will perform an extensive check of the repository's
7734 integrity, validating the hashes and checksums of each entry in
7736 integrity, validating the hashes and checksums of each entry in
7735 the changelog, manifest, and tracked files, as well as the
7737 the changelog, manifest, and tracked files, as well as the
7736 integrity of their crosslinks and indices.
7738 integrity of their crosslinks and indices.
7737
7739
7738 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7740 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7739 for more information about recovery from corruption of the
7741 for more information about recovery from corruption of the
7740 repository.
7742 repository.
7741
7743
7742 Returns 0 on success, 1 if errors are encountered.
7744 Returns 0 on success, 1 if errors are encountered.
7743 """
7745 """
7744 opts = pycompat.byteskwargs(opts)
7746 opts = pycompat.byteskwargs(opts)
7745
7747
7746 level = None
7748 level = None
7747 if opts[b'full']:
7749 if opts[b'full']:
7748 level = verifymod.VERIFY_FULL
7750 level = verifymod.VERIFY_FULL
7749 return hg.verify(repo, level)
7751 return hg.verify(repo, level)
7750
7752
7751
7753
7752 @command(
7754 @command(
7753 b'version',
7755 b'version',
7754 [] + formatteropts,
7756 [] + formatteropts,
7755 helpcategory=command.CATEGORY_HELP,
7757 helpcategory=command.CATEGORY_HELP,
7756 norepo=True,
7758 norepo=True,
7757 intents={INTENT_READONLY},
7759 intents={INTENT_READONLY},
7758 )
7760 )
7759 def version_(ui, **opts):
7761 def version_(ui, **opts):
7760 """output version and copyright information
7762 """output version and copyright information
7761
7763
7762 .. container:: verbose
7764 .. container:: verbose
7763
7765
7764 Template:
7766 Template:
7765
7767
7766 The following keywords are supported. See also :hg:`help templates`.
7768 The following keywords are supported. See also :hg:`help templates`.
7767
7769
7768 :extensions: List of extensions.
7770 :extensions: List of extensions.
7769 :ver: String. Version number.
7771 :ver: String. Version number.
7770
7772
7771 And each entry of ``{extensions}`` provides the following sub-keywords
7773 And each entry of ``{extensions}`` provides the following sub-keywords
7772 in addition to ``{ver}``.
7774 in addition to ``{ver}``.
7773
7775
7774 :bundled: Boolean. True if included in the release.
7776 :bundled: Boolean. True if included in the release.
7775 :name: String. Extension name.
7777 :name: String. Extension name.
7776 """
7778 """
7777 opts = pycompat.byteskwargs(opts)
7779 opts = pycompat.byteskwargs(opts)
7778 if ui.verbose:
7780 if ui.verbose:
7779 ui.pager(b'version')
7781 ui.pager(b'version')
7780 fm = ui.formatter(b"version", opts)
7782 fm = ui.formatter(b"version", opts)
7781 fm.startitem()
7783 fm.startitem()
7782 fm.write(
7784 fm.write(
7783 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7785 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7784 )
7786 )
7785 license = _(
7787 license = _(
7786 b"(see https://mercurial-scm.org for more information)\n"
7788 b"(see https://mercurial-scm.org for more information)\n"
7787 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7789 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7788 b"This is free software; see the source for copying conditions. "
7790 b"This is free software; see the source for copying conditions. "
7789 b"There is NO\nwarranty; "
7791 b"There is NO\nwarranty; "
7790 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7792 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7791 )
7793 )
7792 if not ui.quiet:
7794 if not ui.quiet:
7793 fm.plain(license)
7795 fm.plain(license)
7794
7796
7795 if ui.verbose:
7797 if ui.verbose:
7796 fm.plain(_(b"\nEnabled extensions:\n\n"))
7798 fm.plain(_(b"\nEnabled extensions:\n\n"))
7797 # format names and versions into columns
7799 # format names and versions into columns
7798 names = []
7800 names = []
7799 vers = []
7801 vers = []
7800 isinternals = []
7802 isinternals = []
7801 for name, module in extensions.extensions():
7803 for name, module in extensions.extensions():
7802 names.append(name)
7804 names.append(name)
7803 vers.append(extensions.moduleversion(module) or None)
7805 vers.append(extensions.moduleversion(module) or None)
7804 isinternals.append(extensions.ismoduleinternal(module))
7806 isinternals.append(extensions.ismoduleinternal(module))
7805 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7807 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7806 if names:
7808 if names:
7807 namefmt = b" %%-%ds " % max(len(n) for n in names)
7809 namefmt = b" %%-%ds " % max(len(n) for n in names)
7808 places = [_(b"external"), _(b"internal")]
7810 places = [_(b"external"), _(b"internal")]
7809 for n, v, p in zip(names, vers, isinternals):
7811 for n, v, p in zip(names, vers, isinternals):
7810 fn.startitem()
7812 fn.startitem()
7811 fn.condwrite(ui.verbose, b"name", namefmt, n)
7813 fn.condwrite(ui.verbose, b"name", namefmt, n)
7812 if ui.verbose:
7814 if ui.verbose:
7813 fn.plain(b"%s " % places[p])
7815 fn.plain(b"%s " % places[p])
7814 fn.data(bundled=p)
7816 fn.data(bundled=p)
7815 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7817 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7816 if ui.verbose:
7818 if ui.verbose:
7817 fn.plain(b"\n")
7819 fn.plain(b"\n")
7818 fn.end()
7820 fn.end()
7819 fm.end()
7821 fm.end()
7820
7822
7821
7823
7822 def loadcmdtable(ui, name, cmdtable):
7824 def loadcmdtable(ui, name, cmdtable):
7823 """Load command functions from specified cmdtable
7825 """Load command functions from specified cmdtable
7824 """
7826 """
7825 overrides = [cmd for cmd in cmdtable if cmd in table]
7827 overrides = [cmd for cmd in cmdtable if cmd in table]
7826 if overrides:
7828 if overrides:
7827 ui.warn(
7829 ui.warn(
7828 _(b"extension '%s' overrides commands: %s\n")
7830 _(b"extension '%s' overrides commands: %s\n")
7829 % (name, b" ".join(overrides))
7831 % (name, b" ".join(overrides))
7830 )
7832 )
7831 table.update(cmdtable)
7833 table.update(cmdtable)
@@ -1,1578 +1,1581 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 functools
10 import functools
11 import re
11 import re
12
12
13 from . import (
13 from . import (
14 encoding,
14 encoding,
15 error,
15 error,
16 )
16 )
17
17
18
18
19 def loadconfigtable(ui, extname, configtable):
19 def loadconfigtable(ui, extname, configtable):
20 """update config item known to the ui with the extension ones"""
20 """update config item known to the ui with the extension ones"""
21 for section, items in sorted(configtable.items()):
21 for section, items in sorted(configtable.items()):
22 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 knownitems = ui._knownconfig.setdefault(section, itemregister())
23 knownkeys = set(knownitems)
23 knownkeys = set(knownitems)
24 newkeys = set(items)
24 newkeys = set(items)
25 for key in sorted(knownkeys & newkeys):
25 for key in sorted(knownkeys & newkeys):
26 msg = b"extension '%s' overwrite config item '%s.%s'"
26 msg = b"extension '%s' overwrite config item '%s.%s'"
27 msg %= (extname, section, key)
27 msg %= (extname, section, key)
28 ui.develwarn(msg, config=b'warn-config')
28 ui.develwarn(msg, config=b'warn-config')
29
29
30 knownitems.update(items)
30 knownitems.update(items)
31
31
32
32
33 class configitem(object):
33 class configitem(object):
34 """represent a known config item
34 """represent a known config item
35
35
36 :section: the official config section where to find this item,
36 :section: the official config section where to find this item,
37 :name: the official name within the section,
37 :name: the official name within the section,
38 :default: default value for this item,
38 :default: default value for this item,
39 :alias: optional list of tuples as alternatives,
39 :alias: optional list of tuples as alternatives,
40 :generic: this is a generic definition, match name using regular expression.
40 :generic: this is a generic definition, match name using regular expression.
41 """
41 """
42
42
43 def __init__(
43 def __init__(
44 self,
44 self,
45 section,
45 section,
46 name,
46 name,
47 default=None,
47 default=None,
48 alias=(),
48 alias=(),
49 generic=False,
49 generic=False,
50 priority=0,
50 priority=0,
51 experimental=False,
51 experimental=False,
52 ):
52 ):
53 self.section = section
53 self.section = section
54 self.name = name
54 self.name = name
55 self.default = default
55 self.default = default
56 self.alias = list(alias)
56 self.alias = list(alias)
57 self.generic = generic
57 self.generic = generic
58 self.priority = priority
58 self.priority = priority
59 self.experimental = experimental
59 self.experimental = experimental
60 self._re = None
60 self._re = None
61 if generic:
61 if generic:
62 self._re = re.compile(self.name)
62 self._re = re.compile(self.name)
63
63
64
64
65 class itemregister(dict):
65 class itemregister(dict):
66 """A specialized dictionary that can handle wild-card selection"""
66 """A specialized dictionary that can handle wild-card selection"""
67
67
68 def __init__(self):
68 def __init__(self):
69 super(itemregister, self).__init__()
69 super(itemregister, self).__init__()
70 self._generics = set()
70 self._generics = set()
71
71
72 def update(self, other):
72 def update(self, other):
73 super(itemregister, self).update(other)
73 super(itemregister, self).update(other)
74 self._generics.update(other._generics)
74 self._generics.update(other._generics)
75
75
76 def __setitem__(self, key, item):
76 def __setitem__(self, key, item):
77 super(itemregister, self).__setitem__(key, item)
77 super(itemregister, self).__setitem__(key, item)
78 if item.generic:
78 if item.generic:
79 self._generics.add(item)
79 self._generics.add(item)
80
80
81 def get(self, key):
81 def get(self, key):
82 baseitem = super(itemregister, self).get(key)
82 baseitem = super(itemregister, self).get(key)
83 if baseitem is not None and not baseitem.generic:
83 if baseitem is not None and not baseitem.generic:
84 return baseitem
84 return baseitem
85
85
86 # search for a matching generic item
86 # search for a matching generic item
87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
88 for item in generics:
88 for item in generics:
89 # we use 'match' instead of 'search' to make the matching simpler
89 # we use 'match' instead of 'search' to make the matching simpler
90 # for people unfamiliar with regular expression. Having the match
90 # for people unfamiliar with regular expression. Having the match
91 # rooted to the start of the string will produce less surprising
91 # rooted to the start of the string will produce less surprising
92 # result for user writing simple regex for sub-attribute.
92 # result for user writing simple regex for sub-attribute.
93 #
93 #
94 # For example using "color\..*" match produces an unsurprising
94 # For example using "color\..*" match produces an unsurprising
95 # result, while using search could suddenly match apparently
95 # result, while using search could suddenly match apparently
96 # unrelated configuration that happens to contains "color."
96 # unrelated configuration that happens to contains "color."
97 # anywhere. This is a tradeoff where we favor requiring ".*" on
97 # anywhere. This is a tradeoff where we favor requiring ".*" on
98 # some match to avoid the need to prefix most pattern with "^".
98 # some match to avoid the need to prefix most pattern with "^".
99 # The "^" seems more error prone.
99 # The "^" seems more error prone.
100 if item._re.match(key):
100 if item._re.match(key):
101 return item
101 return item
102
102
103 return None
103 return None
104
104
105
105
106 coreitems = {}
106 coreitems = {}
107
107
108
108
109 def _register(configtable, *args, **kwargs):
109 def _register(configtable, *args, **kwargs):
110 item = configitem(*args, **kwargs)
110 item = configitem(*args, **kwargs)
111 section = configtable.setdefault(item.section, itemregister())
111 section = configtable.setdefault(item.section, itemregister())
112 if item.name in section:
112 if item.name in section:
113 msg = b"duplicated config item registration for '%s.%s'"
113 msg = b"duplicated config item registration for '%s.%s'"
114 raise error.ProgrammingError(msg % (item.section, item.name))
114 raise error.ProgrammingError(msg % (item.section, item.name))
115 section[item.name] = item
115 section[item.name] = item
116
116
117
117
118 # special value for case where the default is derived from other values
118 # special value for case where the default is derived from other values
119 dynamicdefault = object()
119 dynamicdefault = object()
120
120
121 # Registering actual config items
121 # Registering actual config items
122
122
123
123
124 def getitemregister(configtable):
124 def getitemregister(configtable):
125 f = functools.partial(_register, configtable)
125 f = functools.partial(_register, configtable)
126 # export pseudo enum as configitem.*
126 # export pseudo enum as configitem.*
127 f.dynamicdefault = dynamicdefault
127 f.dynamicdefault = dynamicdefault
128 return f
128 return f
129
129
130
130
131 coreconfigitem = getitemregister(coreitems)
131 coreconfigitem = getitemregister(coreitems)
132
132
133
133
134 def _registerdiffopts(section, configprefix=b''):
134 def _registerdiffopts(section, configprefix=b''):
135 coreconfigitem(
135 coreconfigitem(
136 section, configprefix + b'nodates', default=False,
136 section, configprefix + b'nodates', default=False,
137 )
137 )
138 coreconfigitem(
138 coreconfigitem(
139 section, configprefix + b'showfunc', default=False,
139 section, configprefix + b'showfunc', default=False,
140 )
140 )
141 coreconfigitem(
141 coreconfigitem(
142 section, configprefix + b'unified', default=None,
142 section, configprefix + b'unified', default=None,
143 )
143 )
144 coreconfigitem(
144 coreconfigitem(
145 section, configprefix + b'git', default=False,
145 section, configprefix + b'git', default=False,
146 )
146 )
147 coreconfigitem(
147 coreconfigitem(
148 section, configprefix + b'ignorews', default=False,
148 section, configprefix + b'ignorews', default=False,
149 )
149 )
150 coreconfigitem(
150 coreconfigitem(
151 section, configprefix + b'ignorewsamount', default=False,
151 section, configprefix + b'ignorewsamount', default=False,
152 )
152 )
153 coreconfigitem(
153 coreconfigitem(
154 section, configprefix + b'ignoreblanklines', default=False,
154 section, configprefix + b'ignoreblanklines', default=False,
155 )
155 )
156 coreconfigitem(
156 coreconfigitem(
157 section, configprefix + b'ignorewseol', default=False,
157 section, configprefix + b'ignorewseol', default=False,
158 )
158 )
159 coreconfigitem(
159 coreconfigitem(
160 section, configprefix + b'nobinary', default=False,
160 section, configprefix + b'nobinary', default=False,
161 )
161 )
162 coreconfigitem(
162 coreconfigitem(
163 section, configprefix + b'noprefix', default=False,
163 section, configprefix + b'noprefix', default=False,
164 )
164 )
165 coreconfigitem(
165 coreconfigitem(
166 section, configprefix + b'word-diff', default=False,
166 section, configprefix + b'word-diff', default=False,
167 )
167 )
168
168
169
169
170 coreconfigitem(
170 coreconfigitem(
171 b'alias', b'.*', default=dynamicdefault, generic=True,
171 b'alias', b'.*', default=dynamicdefault, generic=True,
172 )
172 )
173 coreconfigitem(
173 coreconfigitem(
174 b'auth', b'cookiefile', default=None,
174 b'auth', b'cookiefile', default=None,
175 )
175 )
176 _registerdiffopts(section=b'annotate')
176 _registerdiffopts(section=b'annotate')
177 # bookmarks.pushing: internal hack for discovery
177 # bookmarks.pushing: internal hack for discovery
178 coreconfigitem(
178 coreconfigitem(
179 b'bookmarks', b'pushing', default=list,
179 b'bookmarks', b'pushing', default=list,
180 )
180 )
181 # bundle.mainreporoot: internal hack for bundlerepo
181 # bundle.mainreporoot: internal hack for bundlerepo
182 coreconfigitem(
182 coreconfigitem(
183 b'bundle', b'mainreporoot', default=b'',
183 b'bundle', b'mainreporoot', default=b'',
184 )
184 )
185 coreconfigitem(
185 coreconfigitem(
186 b'censor', b'policy', default=b'abort', experimental=True,
186 b'censor', b'policy', default=b'abort', experimental=True,
187 )
187 )
188 coreconfigitem(
188 coreconfigitem(
189 b'chgserver', b'idletimeout', default=3600,
189 b'chgserver', b'idletimeout', default=3600,
190 )
190 )
191 coreconfigitem(
191 coreconfigitem(
192 b'chgserver', b'skiphash', default=False,
192 b'chgserver', b'skiphash', default=False,
193 )
193 )
194 coreconfigitem(
194 coreconfigitem(
195 b'cmdserver', b'log', default=None,
195 b'cmdserver', b'log', default=None,
196 )
196 )
197 coreconfigitem(
197 coreconfigitem(
198 b'cmdserver', b'max-log-files', default=7,
198 b'cmdserver', b'max-log-files', default=7,
199 )
199 )
200 coreconfigitem(
200 coreconfigitem(
201 b'cmdserver', b'max-log-size', default=b'1 MB',
201 b'cmdserver', b'max-log-size', default=b'1 MB',
202 )
202 )
203 coreconfigitem(
203 coreconfigitem(
204 b'cmdserver', b'max-repo-cache', default=0, experimental=True,
204 b'cmdserver', b'max-repo-cache', default=0, experimental=True,
205 )
205 )
206 coreconfigitem(
206 coreconfigitem(
207 b'cmdserver', b'message-encodings', default=list, experimental=True,
207 b'cmdserver', b'message-encodings', default=list, experimental=True,
208 )
208 )
209 coreconfigitem(
209 coreconfigitem(
210 b'cmdserver',
210 b'cmdserver',
211 b'track-log',
211 b'track-log',
212 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
212 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
213 )
213 )
214 coreconfigitem(
214 coreconfigitem(
215 b'color', b'.*', default=None, generic=True,
215 b'color', b'.*', default=None, generic=True,
216 )
216 )
217 coreconfigitem(
217 coreconfigitem(
218 b'color', b'mode', default=b'auto',
218 b'color', b'mode', default=b'auto',
219 )
219 )
220 coreconfigitem(
220 coreconfigitem(
221 b'color', b'pagermode', default=dynamicdefault,
221 b'color', b'pagermode', default=dynamicdefault,
222 )
222 )
223 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
223 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
224 coreconfigitem(
224 coreconfigitem(
225 b'commands', b'commit.post-status', default=False,
225 b'commands', b'commit.post-status', default=False,
226 )
226 )
227 coreconfigitem(
227 coreconfigitem(
228 b'commands', b'grep.all-files', default=False, experimental=True,
228 b'commands', b'grep.all-files', default=False, experimental=True,
229 )
229 )
230 coreconfigitem(
230 coreconfigitem(
231 b'commands', b'merge.require-rev', default=False,
231 b'commands', b'merge.require-rev', default=False,
232 )
232 )
233 coreconfigitem(
233 coreconfigitem(
234 b'commands', b'push.require-revs', default=False,
234 b'commands', b'push.require-revs', default=False,
235 )
235 )
236 coreconfigitem(
236 coreconfigitem(
237 b'commands', b'resolve.confirm', default=False,
237 b'commands', b'resolve.confirm', default=False,
238 )
238 )
239 coreconfigitem(
239 coreconfigitem(
240 b'commands', b'resolve.explicit-re-merge', default=False,
240 b'commands', b'resolve.explicit-re-merge', default=False,
241 )
241 )
242 coreconfigitem(
242 coreconfigitem(
243 b'commands', b'resolve.mark-check', default=b'none',
243 b'commands', b'resolve.mark-check', default=b'none',
244 )
244 )
245 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
245 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
246 coreconfigitem(
246 coreconfigitem(
247 b'commands', b'show.aliasprefix', default=list,
247 b'commands', b'show.aliasprefix', default=list,
248 )
248 )
249 coreconfigitem(
249 coreconfigitem(
250 b'commands', b'status.relative', default=False,
250 b'commands', b'status.relative', default=False,
251 )
251 )
252 coreconfigitem(
252 coreconfigitem(
253 b'commands', b'status.skipstates', default=[], experimental=True,
253 b'commands', b'status.skipstates', default=[], experimental=True,
254 )
254 )
255 coreconfigitem(
255 coreconfigitem(
256 b'commands', b'status.terse', default=b'',
256 b'commands', b'status.terse', default=b'',
257 )
257 )
258 coreconfigitem(
258 coreconfigitem(
259 b'commands', b'status.verbose', default=False,
259 b'commands', b'status.verbose', default=False,
260 )
260 )
261 coreconfigitem(
261 coreconfigitem(
262 b'commands', b'update.check', default=None,
262 b'commands', b'update.check', default=None,
263 )
263 )
264 coreconfigitem(
264 coreconfigitem(
265 b'commands', b'update.requiredest', default=False,
265 b'commands', b'update.requiredest', default=False,
266 )
266 )
267 coreconfigitem(
267 coreconfigitem(
268 b'committemplate', b'.*', default=None, generic=True,
268 b'committemplate', b'.*', default=None, generic=True,
269 )
269 )
270 coreconfigitem(
270 coreconfigitem(
271 b'convert', b'bzr.saverev', default=True,
271 b'convert', b'bzr.saverev', default=True,
272 )
272 )
273 coreconfigitem(
273 coreconfigitem(
274 b'convert', b'cvsps.cache', default=True,
274 b'convert', b'cvsps.cache', default=True,
275 )
275 )
276 coreconfigitem(
276 coreconfigitem(
277 b'convert', b'cvsps.fuzz', default=60,
277 b'convert', b'cvsps.fuzz', default=60,
278 )
278 )
279 coreconfigitem(
279 coreconfigitem(
280 b'convert', b'cvsps.logencoding', default=None,
280 b'convert', b'cvsps.logencoding', default=None,
281 )
281 )
282 coreconfigitem(
282 coreconfigitem(
283 b'convert', b'cvsps.mergefrom', default=None,
283 b'convert', b'cvsps.mergefrom', default=None,
284 )
284 )
285 coreconfigitem(
285 coreconfigitem(
286 b'convert', b'cvsps.mergeto', default=None,
286 b'convert', b'cvsps.mergeto', default=None,
287 )
287 )
288 coreconfigitem(
288 coreconfigitem(
289 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
289 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
290 )
290 )
291 coreconfigitem(
291 coreconfigitem(
292 b'convert', b'git.extrakeys', default=list,
292 b'convert', b'git.extrakeys', default=list,
293 )
293 )
294 coreconfigitem(
294 coreconfigitem(
295 b'convert', b'git.findcopiesharder', default=False,
295 b'convert', b'git.findcopiesharder', default=False,
296 )
296 )
297 coreconfigitem(
297 coreconfigitem(
298 b'convert', b'git.remoteprefix', default=b'remote',
298 b'convert', b'git.remoteprefix', default=b'remote',
299 )
299 )
300 coreconfigitem(
300 coreconfigitem(
301 b'convert', b'git.renamelimit', default=400,
301 b'convert', b'git.renamelimit', default=400,
302 )
302 )
303 coreconfigitem(
303 coreconfigitem(
304 b'convert', b'git.saverev', default=True,
304 b'convert', b'git.saverev', default=True,
305 )
305 )
306 coreconfigitem(
306 coreconfigitem(
307 b'convert', b'git.similarity', default=50,
307 b'convert', b'git.similarity', default=50,
308 )
308 )
309 coreconfigitem(
309 coreconfigitem(
310 b'convert', b'git.skipsubmodules', default=False,
310 b'convert', b'git.skipsubmodules', default=False,
311 )
311 )
312 coreconfigitem(
312 coreconfigitem(
313 b'convert', b'hg.clonebranches', default=False,
313 b'convert', b'hg.clonebranches', default=False,
314 )
314 )
315 coreconfigitem(
315 coreconfigitem(
316 b'convert', b'hg.ignoreerrors', default=False,
316 b'convert', b'hg.ignoreerrors', default=False,
317 )
317 )
318 coreconfigitem(
318 coreconfigitem(
319 b'convert', b'hg.preserve-hash', default=False,
319 b'convert', b'hg.preserve-hash', default=False,
320 )
320 )
321 coreconfigitem(
321 coreconfigitem(
322 b'convert', b'hg.revs', default=None,
322 b'convert', b'hg.revs', default=None,
323 )
323 )
324 coreconfigitem(
324 coreconfigitem(
325 b'convert', b'hg.saverev', default=False,
325 b'convert', b'hg.saverev', default=False,
326 )
326 )
327 coreconfigitem(
327 coreconfigitem(
328 b'convert', b'hg.sourcename', default=None,
328 b'convert', b'hg.sourcename', default=None,
329 )
329 )
330 coreconfigitem(
330 coreconfigitem(
331 b'convert', b'hg.startrev', default=None,
331 b'convert', b'hg.startrev', default=None,
332 )
332 )
333 coreconfigitem(
333 coreconfigitem(
334 b'convert', b'hg.tagsbranch', default=b'default',
334 b'convert', b'hg.tagsbranch', default=b'default',
335 )
335 )
336 coreconfigitem(
336 coreconfigitem(
337 b'convert', b'hg.usebranchnames', default=True,
337 b'convert', b'hg.usebranchnames', default=True,
338 )
338 )
339 coreconfigitem(
339 coreconfigitem(
340 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
340 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
341 )
341 )
342 coreconfigitem(
342 coreconfigitem(
343 b'convert', b'localtimezone', default=False,
343 b'convert', b'localtimezone', default=False,
344 )
344 )
345 coreconfigitem(
345 coreconfigitem(
346 b'convert', b'p4.encoding', default=dynamicdefault,
346 b'convert', b'p4.encoding', default=dynamicdefault,
347 )
347 )
348 coreconfigitem(
348 coreconfigitem(
349 b'convert', b'p4.startrev', default=0,
349 b'convert', b'p4.startrev', default=0,
350 )
350 )
351 coreconfigitem(
351 coreconfigitem(
352 b'convert', b'skiptags', default=False,
352 b'convert', b'skiptags', default=False,
353 )
353 )
354 coreconfigitem(
354 coreconfigitem(
355 b'convert', b'svn.debugsvnlog', default=True,
355 b'convert', b'svn.debugsvnlog', default=True,
356 )
356 )
357 coreconfigitem(
357 coreconfigitem(
358 b'convert', b'svn.trunk', default=None,
358 b'convert', b'svn.trunk', default=None,
359 )
359 )
360 coreconfigitem(
360 coreconfigitem(
361 b'convert', b'svn.tags', default=None,
361 b'convert', b'svn.tags', default=None,
362 )
362 )
363 coreconfigitem(
363 coreconfigitem(
364 b'convert', b'svn.branches', default=None,
364 b'convert', b'svn.branches', default=None,
365 )
365 )
366 coreconfigitem(
366 coreconfigitem(
367 b'convert', b'svn.startrev', default=0,
367 b'convert', b'svn.startrev', default=0,
368 )
368 )
369 coreconfigitem(
369 coreconfigitem(
370 b'debug', b'dirstate.delaywrite', default=0,
370 b'debug', b'dirstate.delaywrite', default=0,
371 )
371 )
372 coreconfigitem(
372 coreconfigitem(
373 b'defaults', b'.*', default=None, generic=True,
373 b'defaults', b'.*', default=None, generic=True,
374 )
374 )
375 coreconfigitem(
375 coreconfigitem(
376 b'devel', b'all-warnings', default=False,
376 b'devel', b'all-warnings', default=False,
377 )
377 )
378 coreconfigitem(
378 coreconfigitem(
379 b'devel', b'bundle2.debug', default=False,
379 b'devel', b'bundle2.debug', default=False,
380 )
380 )
381 coreconfigitem(
381 coreconfigitem(
382 b'devel', b'bundle.delta', default=b'',
382 b'devel', b'bundle.delta', default=b'',
383 )
383 )
384 coreconfigitem(
384 coreconfigitem(
385 b'devel', b'cache-vfs', default=None,
385 b'devel', b'cache-vfs', default=None,
386 )
386 )
387 coreconfigitem(
387 coreconfigitem(
388 b'devel', b'check-locks', default=False,
388 b'devel', b'check-locks', default=False,
389 )
389 )
390 coreconfigitem(
390 coreconfigitem(
391 b'devel', b'check-relroot', default=False,
391 b'devel', b'check-relroot', default=False,
392 )
392 )
393 coreconfigitem(
393 coreconfigitem(
394 b'devel', b'default-date', default=None,
394 b'devel', b'default-date', default=None,
395 )
395 )
396 coreconfigitem(
396 coreconfigitem(
397 b'devel', b'deprec-warn', default=False,
397 b'devel', b'deprec-warn', default=False,
398 )
398 )
399 coreconfigitem(
399 coreconfigitem(
400 b'devel', b'disableloaddefaultcerts', default=False,
400 b'devel', b'disableloaddefaultcerts', default=False,
401 )
401 )
402 coreconfigitem(
402 coreconfigitem(
403 b'devel', b'warn-empty-changegroup', default=False,
403 b'devel', b'warn-empty-changegroup', default=False,
404 )
404 )
405 coreconfigitem(
405 coreconfigitem(
406 b'devel', b'legacy.exchange', default=list,
406 b'devel', b'legacy.exchange', default=list,
407 )
407 )
408 # TODO before getting `persistent-nodemap` out of experimental
408 # TODO before getting `persistent-nodemap` out of experimental
409 #
409 #
410 # * code/tests around aborted transaction
410 # * code/tests around aborted transaction
411 # * regenerate a new nodemap when the unused/total ration is to high
411 # * regenerate a new nodemap when the unused/total ration is to high
412 # * decide for a "status" of the persistent nodemap and associated location
412 # * decide for a "status" of the persistent nodemap and associated location
413 # - part of the store next the revlog itself (new requirements)
413 # - part of the store next the revlog itself (new requirements)
414 # - part of the cache directory
414 # - part of the cache directory
415 # - part of an `index` directory
415 # - part of an `index` directory
416 # (https://www.mercurial-scm.org/wiki/ComputedIndexPlan)
416 # (https://www.mercurial-scm.org/wiki/ComputedIndexPlan)
417 # * do we want to use this for more than just changelog? if so we need:
417 # * do we want to use this for more than just changelog? if so we need:
418 # - simpler "pending" logic for them
418 # - simpler "pending" logic for them
419 # - double check the memory story (we dont want to keep all revlog in memory)
419 # - double check the memory story (we dont want to keep all revlog in memory)
420 # - think about the naming scheme if we are in "cache"
420 # - think about the naming scheme if we are in "cache"
421 # * increment the version format to "1" and freeze it.
421 # * increment the version format to "1" and freeze it.
422 coreconfigitem(
422 coreconfigitem(
423 b'devel', b'persistent-nodemap', default=False,
423 b'devel', b'persistent-nodemap', default=False,
424 )
424 )
425 coreconfigitem(
425 coreconfigitem(
426 b'devel', b'servercafile', default=b'',
426 b'devel', b'servercafile', default=b'',
427 )
427 )
428 coreconfigitem(
428 coreconfigitem(
429 b'devel', b'serverexactprotocol', default=b'',
429 b'devel', b'serverexactprotocol', default=b'',
430 )
430 )
431 coreconfigitem(
431 coreconfigitem(
432 b'devel', b'serverrequirecert', default=False,
432 b'devel', b'serverrequirecert', default=False,
433 )
433 )
434 coreconfigitem(
434 coreconfigitem(
435 b'devel', b'strip-obsmarkers', default=True,
435 b'devel', b'strip-obsmarkers', default=True,
436 )
436 )
437 coreconfigitem(
437 coreconfigitem(
438 b'devel', b'warn-config', default=None,
438 b'devel', b'warn-config', default=None,
439 )
439 )
440 coreconfigitem(
440 coreconfigitem(
441 b'devel', b'warn-config-default', default=None,
441 b'devel', b'warn-config-default', default=None,
442 )
442 )
443 coreconfigitem(
443 coreconfigitem(
444 b'devel', b'user.obsmarker', default=None,
444 b'devel', b'user.obsmarker', default=None,
445 )
445 )
446 coreconfigitem(
446 coreconfigitem(
447 b'devel', b'warn-config-unknown', default=None,
447 b'devel', b'warn-config-unknown', default=None,
448 )
448 )
449 coreconfigitem(
449 coreconfigitem(
450 b'devel', b'debug.copies', default=False,
450 b'devel', b'debug.copies', default=False,
451 )
451 )
452 coreconfigitem(
452 coreconfigitem(
453 b'devel', b'debug.extensions', default=False,
453 b'devel', b'debug.extensions', default=False,
454 )
454 )
455 coreconfigitem(
455 coreconfigitem(
456 b'devel', b'debug.repo-filters', default=False,
456 b'devel', b'debug.repo-filters', default=False,
457 )
457 )
458 coreconfigitem(
458 coreconfigitem(
459 b'devel', b'debug.peer-request', default=False,
459 b'devel', b'debug.peer-request', default=False,
460 )
460 )
461 coreconfigitem(
461 coreconfigitem(
462 b'devel', b'discovery.randomize', default=True,
462 b'devel', b'discovery.randomize', default=True,
463 )
463 )
464 _registerdiffopts(section=b'diff')
464 _registerdiffopts(section=b'diff')
465 coreconfigitem(
465 coreconfigitem(
466 b'email', b'bcc', default=None,
466 b'email', b'bcc', default=None,
467 )
467 )
468 coreconfigitem(
468 coreconfigitem(
469 b'email', b'cc', default=None,
469 b'email', b'cc', default=None,
470 )
470 )
471 coreconfigitem(
471 coreconfigitem(
472 b'email', b'charsets', default=list,
472 b'email', b'charsets', default=list,
473 )
473 )
474 coreconfigitem(
474 coreconfigitem(
475 b'email', b'from', default=None,
475 b'email', b'from', default=None,
476 )
476 )
477 coreconfigitem(
477 coreconfigitem(
478 b'email', b'method', default=b'smtp',
478 b'email', b'method', default=b'smtp',
479 )
479 )
480 coreconfigitem(
480 coreconfigitem(
481 b'email', b'reply-to', default=None,
481 b'email', b'reply-to', default=None,
482 )
482 )
483 coreconfigitem(
483 coreconfigitem(
484 b'email', b'to', default=None,
484 b'email', b'to', default=None,
485 )
485 )
486 coreconfigitem(
486 coreconfigitem(
487 b'experimental', b'archivemetatemplate', default=dynamicdefault,
487 b'experimental', b'archivemetatemplate', default=dynamicdefault,
488 )
488 )
489 coreconfigitem(
489 coreconfigitem(
490 b'experimental', b'auto-publish', default=b'publish',
490 b'experimental', b'auto-publish', default=b'publish',
491 )
491 )
492 coreconfigitem(
492 coreconfigitem(
493 b'experimental', b'bundle-phases', default=False,
493 b'experimental', b'bundle-phases', default=False,
494 )
494 )
495 coreconfigitem(
495 coreconfigitem(
496 b'experimental', b'bundle2-advertise', default=True,
496 b'experimental', b'bundle2-advertise', default=True,
497 )
497 )
498 coreconfigitem(
498 coreconfigitem(
499 b'experimental', b'bundle2-output-capture', default=False,
499 b'experimental', b'bundle2-output-capture', default=False,
500 )
500 )
501 coreconfigitem(
501 coreconfigitem(
502 b'experimental', b'bundle2.pushback', default=False,
502 b'experimental', b'bundle2.pushback', default=False,
503 )
503 )
504 coreconfigitem(
504 coreconfigitem(
505 b'experimental', b'bundle2lazylocking', default=False,
505 b'experimental', b'bundle2lazylocking', default=False,
506 )
506 )
507 coreconfigitem(
507 coreconfigitem(
508 b'experimental', b'bundlecomplevel', default=None,
508 b'experimental', b'bundlecomplevel', default=None,
509 )
509 )
510 coreconfigitem(
510 coreconfigitem(
511 b'experimental', b'bundlecomplevel.bzip2', default=None,
511 b'experimental', b'bundlecomplevel.bzip2', default=None,
512 )
512 )
513 coreconfigitem(
513 coreconfigitem(
514 b'experimental', b'bundlecomplevel.gzip', default=None,
514 b'experimental', b'bundlecomplevel.gzip', default=None,
515 )
515 )
516 coreconfigitem(
516 coreconfigitem(
517 b'experimental', b'bundlecomplevel.none', default=None,
517 b'experimental', b'bundlecomplevel.none', default=None,
518 )
518 )
519 coreconfigitem(
519 coreconfigitem(
520 b'experimental', b'bundlecomplevel.zstd', default=None,
520 b'experimental', b'bundlecomplevel.zstd', default=None,
521 )
521 )
522 coreconfigitem(
522 coreconfigitem(
523 b'experimental', b'changegroup3', default=False,
523 b'experimental', b'changegroup3', default=False,
524 )
524 )
525 coreconfigitem(
525 coreconfigitem(
526 b'experimental', b'cleanup-as-archived', default=False,
526 b'experimental', b'cleanup-as-archived', default=False,
527 )
527 )
528 coreconfigitem(
528 coreconfigitem(
529 b'experimental', b'clientcompressionengines', default=list,
529 b'experimental', b'clientcompressionengines', default=list,
530 )
530 )
531 coreconfigitem(
531 coreconfigitem(
532 b'experimental', b'copytrace', default=b'on',
532 b'experimental', b'copytrace', default=b'on',
533 )
533 )
534 coreconfigitem(
534 coreconfigitem(
535 b'experimental', b'copytrace.movecandidateslimit', default=100,
535 b'experimental', b'copytrace.movecandidateslimit', default=100,
536 )
536 )
537 coreconfigitem(
537 coreconfigitem(
538 b'experimental', b'copytrace.sourcecommitlimit', default=100,
538 b'experimental', b'copytrace.sourcecommitlimit', default=100,
539 )
539 )
540 coreconfigitem(
540 coreconfigitem(
541 b'experimental', b'copies.read-from', default=b"filelog-only",
541 b'experimental', b'copies.read-from', default=b"filelog-only",
542 )
542 )
543 coreconfigitem(
543 coreconfigitem(
544 b'experimental', b'copies.write-to', default=b'filelog-only',
544 b'experimental', b'copies.write-to', default=b'filelog-only',
545 )
545 )
546 coreconfigitem(
546 coreconfigitem(
547 b'experimental', b'crecordtest', default=None,
547 b'experimental', b'crecordtest', default=None,
548 )
548 )
549 coreconfigitem(
549 coreconfigitem(
550 b'experimental', b'directaccess', default=False,
550 b'experimental', b'directaccess', default=False,
551 )
551 )
552 coreconfigitem(
552 coreconfigitem(
553 b'experimental', b'directaccess.revnums', default=False,
553 b'experimental', b'directaccess.revnums', default=False,
554 )
554 )
555 coreconfigitem(
555 coreconfigitem(
556 b'experimental', b'editortmpinhg', default=False,
556 b'experimental', b'editortmpinhg', default=False,
557 )
557 )
558 coreconfigitem(
558 coreconfigitem(
559 b'experimental', b'evolution', default=list,
559 b'experimental', b'evolution', default=list,
560 )
560 )
561 coreconfigitem(
561 coreconfigitem(
562 b'experimental',
562 b'experimental',
563 b'evolution.allowdivergence',
563 b'evolution.allowdivergence',
564 default=False,
564 default=False,
565 alias=[(b'experimental', b'allowdivergence')],
565 alias=[(b'experimental', b'allowdivergence')],
566 )
566 )
567 coreconfigitem(
567 coreconfigitem(
568 b'experimental', b'evolution.allowunstable', default=None,
568 b'experimental', b'evolution.allowunstable', default=None,
569 )
569 )
570 coreconfigitem(
570 coreconfigitem(
571 b'experimental', b'evolution.createmarkers', default=None,
571 b'experimental', b'evolution.createmarkers', default=None,
572 )
572 )
573 coreconfigitem(
573 coreconfigitem(
574 b'experimental',
574 b'experimental',
575 b'evolution.effect-flags',
575 b'evolution.effect-flags',
576 default=True,
576 default=True,
577 alias=[(b'experimental', b'effect-flags')],
577 alias=[(b'experimental', b'effect-flags')],
578 )
578 )
579 coreconfigitem(
579 coreconfigitem(
580 b'experimental', b'evolution.exchange', default=None,
580 b'experimental', b'evolution.exchange', default=None,
581 )
581 )
582 coreconfigitem(
582 coreconfigitem(
583 b'experimental', b'evolution.bundle-obsmarker', default=False,
583 b'experimental', b'evolution.bundle-obsmarker', default=False,
584 )
584 )
585 coreconfigitem(
585 coreconfigitem(
586 b'experimental', b'log.topo', default=False,
586 b'experimental', b'log.topo', default=False,
587 )
587 )
588 coreconfigitem(
588 coreconfigitem(
589 b'experimental', b'evolution.report-instabilities', default=True,
589 b'experimental', b'evolution.report-instabilities', default=True,
590 )
590 )
591 coreconfigitem(
591 coreconfigitem(
592 b'experimental', b'evolution.track-operation', default=True,
592 b'experimental', b'evolution.track-operation', default=True,
593 )
593 )
594 # repo-level config to exclude a revset visibility
594 # repo-level config to exclude a revset visibility
595 #
595 #
596 # The target use case is to use `share` to expose different subset of the same
596 # The target use case is to use `share` to expose different subset of the same
597 # repository, especially server side. See also `server.view`.
597 # repository, especially server side. See also `server.view`.
598 coreconfigitem(
598 coreconfigitem(
599 b'experimental', b'extra-filter-revs', default=None,
599 b'experimental', b'extra-filter-revs', default=None,
600 )
600 )
601 coreconfigitem(
601 coreconfigitem(
602 b'experimental', b'maxdeltachainspan', default=-1,
602 b'experimental', b'maxdeltachainspan', default=-1,
603 )
603 )
604 coreconfigitem(
604 coreconfigitem(
605 b'experimental', b'mergetempdirprefix', default=None,
605 b'experimental', b'mergetempdirprefix', default=None,
606 )
606 )
607 coreconfigitem(
607 coreconfigitem(
608 b'experimental', b'mmapindexthreshold', default=None,
608 b'experimental', b'mmapindexthreshold', default=None,
609 )
609 )
610 coreconfigitem(
610 coreconfigitem(
611 b'experimental', b'narrow', default=False,
611 b'experimental', b'narrow', default=False,
612 )
612 )
613 coreconfigitem(
613 coreconfigitem(
614 b'experimental', b'nonnormalparanoidcheck', default=False,
614 b'experimental', b'nonnormalparanoidcheck', default=False,
615 )
615 )
616 coreconfigitem(
616 coreconfigitem(
617 b'experimental', b'exportableenviron', default=list,
617 b'experimental', b'exportableenviron', default=list,
618 )
618 )
619 coreconfigitem(
619 coreconfigitem(
620 b'experimental', b'extendedheader.index', default=None,
620 b'experimental', b'extendedheader.index', default=None,
621 )
621 )
622 coreconfigitem(
622 coreconfigitem(
623 b'experimental', b'extendedheader.similarity', default=False,
623 b'experimental', b'extendedheader.similarity', default=False,
624 )
624 )
625 coreconfigitem(
625 coreconfigitem(
626 b'experimental', b'graphshorten', default=False,
626 b'experimental', b'graphshorten', default=False,
627 )
627 )
628 coreconfigitem(
628 coreconfigitem(
629 b'experimental', b'graphstyle.parent', default=dynamicdefault,
629 b'experimental', b'graphstyle.parent', default=dynamicdefault,
630 )
630 )
631 coreconfigitem(
631 coreconfigitem(
632 b'experimental', b'graphstyle.missing', default=dynamicdefault,
632 b'experimental', b'graphstyle.missing', default=dynamicdefault,
633 )
633 )
634 coreconfigitem(
634 coreconfigitem(
635 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
635 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
636 )
636 )
637 coreconfigitem(
637 coreconfigitem(
638 b'experimental', b'hook-track-tags', default=False,
638 b'experimental', b'hook-track-tags', default=False,
639 )
639 )
640 coreconfigitem(
640 coreconfigitem(
641 b'experimental', b'httppeer.advertise-v2', default=False,
641 b'experimental', b'httppeer.advertise-v2', default=False,
642 )
642 )
643 coreconfigitem(
643 coreconfigitem(
644 b'experimental', b'httppeer.v2-encoder-order', default=None,
644 b'experimental', b'httppeer.v2-encoder-order', default=None,
645 )
645 )
646 coreconfigitem(
646 coreconfigitem(
647 b'experimental', b'httppostargs', default=False,
647 b'experimental', b'httppostargs', default=False,
648 )
648 )
649 coreconfigitem(
649 coreconfigitem(
650 b'experimental', b'mergedriver', default=None,
650 b'experimental', b'mergedriver', default=None,
651 )
651 )
652 coreconfigitem(b'experimental', b'nointerrupt', default=False)
652 coreconfigitem(b'experimental', b'nointerrupt', default=False)
653 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
653 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
654
654
655 coreconfigitem(
655 coreconfigitem(
656 b'experimental', b'obsmarkers-exchange-debug', default=False,
656 b'experimental', b'obsmarkers-exchange-debug', default=False,
657 )
657 )
658 coreconfigitem(
658 coreconfigitem(
659 b'experimental', b'remotenames', default=False,
659 b'experimental', b'remotenames', default=False,
660 )
660 )
661 coreconfigitem(
661 coreconfigitem(
662 b'experimental', b'removeemptydirs', default=True,
662 b'experimental', b'removeemptydirs', default=True,
663 )
663 )
664 coreconfigitem(
664 coreconfigitem(
665 b'experimental', b'revert.interactive.select-to-keep', default=False,
665 b'experimental', b'revert.interactive.select-to-keep', default=False,
666 )
666 )
667 coreconfigitem(
667 coreconfigitem(
668 b'experimental', b'revisions.prefixhexnode', default=False,
668 b'experimental', b'revisions.prefixhexnode', default=False,
669 )
669 )
670 coreconfigitem(
670 coreconfigitem(
671 b'experimental', b'revlogv2', default=None,
671 b'experimental', b'revlogv2', default=None,
672 )
672 )
673 coreconfigitem(
673 coreconfigitem(
674 b'experimental', b'revisions.disambiguatewithin', default=None,
674 b'experimental', b'revisions.disambiguatewithin', default=None,
675 )
675 )
676 coreconfigitem(
676 coreconfigitem(
677 b'experimental', b'rust.index', default=False,
677 b'experimental', b'rust.index', default=False,
678 )
678 )
679 coreconfigitem(
679 coreconfigitem(
680 b'experimental', b'exp-persistent-nodemap', default=False,
680 b'experimental', b'exp-persistent-nodemap', default=False,
681 )
681 )
682 coreconfigitem(
682 coreconfigitem(
683 b'experimental', b'exp-persistent-nodemap.mmap', default=True,
683 b'experimental', b'exp-persistent-nodemap.mmap', default=True,
684 )
684 )
685 coreconfigitem(
685 coreconfigitem(
686 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
686 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
687 )
687 )
688 coreconfigitem(
688 coreconfigitem(
689 b'experimental',
689 b'experimental',
690 b'server.manifestdata.recommended-batch-size',
690 b'server.manifestdata.recommended-batch-size',
691 default=100000,
691 default=100000,
692 )
692 )
693 coreconfigitem(
693 coreconfigitem(
694 b'experimental', b'server.stream-narrow-clones', default=False,
694 b'experimental', b'server.stream-narrow-clones', default=False,
695 )
695 )
696 coreconfigitem(
696 coreconfigitem(
697 b'experimental', b'single-head-per-branch', default=False,
697 b'experimental', b'single-head-per-branch', default=False,
698 )
698 )
699 coreconfigitem(
699 coreconfigitem(
700 b'experimental',
700 b'experimental',
701 b'single-head-per-branch:account-closed-heads',
701 b'single-head-per-branch:account-closed-heads',
702 default=False,
702 default=False,
703 )
703 )
704 coreconfigitem(
704 coreconfigitem(
705 b'experimental', b'sshserver.support-v2', default=False,
705 b'experimental', b'sshserver.support-v2', default=False,
706 )
706 )
707 coreconfigitem(
707 coreconfigitem(
708 b'experimental', b'sparse-read', default=False,
708 b'experimental', b'sparse-read', default=False,
709 )
709 )
710 coreconfigitem(
710 coreconfigitem(
711 b'experimental', b'sparse-read.density-threshold', default=0.50,
711 b'experimental', b'sparse-read.density-threshold', default=0.50,
712 )
712 )
713 coreconfigitem(
713 coreconfigitem(
714 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
714 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
715 )
715 )
716 coreconfigitem(
716 coreconfigitem(
717 b'experimental', b'treemanifest', default=False,
717 b'experimental', b'treemanifest', default=False,
718 )
718 )
719 coreconfigitem(
719 coreconfigitem(
720 b'experimental', b'update.atomic-file', default=False,
720 b'experimental', b'update.atomic-file', default=False,
721 )
721 )
722 coreconfigitem(
722 coreconfigitem(
723 b'experimental', b'sshpeer.advertise-v2', default=False,
723 b'experimental', b'sshpeer.advertise-v2', default=False,
724 )
724 )
725 coreconfigitem(
725 coreconfigitem(
726 b'experimental', b'web.apiserver', default=False,
726 b'experimental', b'web.apiserver', default=False,
727 )
727 )
728 coreconfigitem(
728 coreconfigitem(
729 b'experimental', b'web.api.http-v2', default=False,
729 b'experimental', b'web.api.http-v2', default=False,
730 )
730 )
731 coreconfigitem(
731 coreconfigitem(
732 b'experimental', b'web.api.debugreflect', default=False,
732 b'experimental', b'web.api.debugreflect', default=False,
733 )
733 )
734 coreconfigitem(
734 coreconfigitem(
735 b'experimental', b'worker.wdir-get-thread-safe', default=False,
735 b'experimental', b'worker.wdir-get-thread-safe', default=False,
736 )
736 )
737 coreconfigitem(
737 coreconfigitem(
738 b'experimental', b'worker.repository-upgrade', default=False,
738 b'experimental', b'worker.repository-upgrade', default=False,
739 )
739 )
740 coreconfigitem(
740 coreconfigitem(
741 b'experimental', b'xdiff', default=False,
741 b'experimental', b'xdiff', default=False,
742 )
742 )
743 coreconfigitem(
743 coreconfigitem(
744 b'extensions', b'.*', default=None, generic=True,
744 b'extensions', b'.*', default=None, generic=True,
745 )
745 )
746 coreconfigitem(
746 coreconfigitem(
747 b'extdata', b'.*', default=None, generic=True,
747 b'extdata', b'.*', default=None, generic=True,
748 )
748 )
749 coreconfigitem(
749 coreconfigitem(
750 b'format', b'bookmarks-in-store', default=False,
750 b'format', b'bookmarks-in-store', default=False,
751 )
751 )
752 coreconfigitem(
752 coreconfigitem(
753 b'format', b'chunkcachesize', default=None, experimental=True,
753 b'format', b'chunkcachesize', default=None, experimental=True,
754 )
754 )
755 coreconfigitem(
755 coreconfigitem(
756 b'format', b'dotencode', default=True,
756 b'format', b'dotencode', default=True,
757 )
757 )
758 coreconfigitem(
758 coreconfigitem(
759 b'format', b'generaldelta', default=False, experimental=True,
759 b'format', b'generaldelta', default=False, experimental=True,
760 )
760 )
761 coreconfigitem(
761 coreconfigitem(
762 b'format', b'manifestcachesize', default=None, experimental=True,
762 b'format', b'manifestcachesize', default=None, experimental=True,
763 )
763 )
764 coreconfigitem(
764 coreconfigitem(
765 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
765 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
766 )
766 )
767 coreconfigitem(
767 coreconfigitem(
768 b'format', b'obsstore-version', default=None,
768 b'format', b'obsstore-version', default=None,
769 )
769 )
770 coreconfigitem(
770 coreconfigitem(
771 b'format', b'sparse-revlog', default=True,
771 b'format', b'sparse-revlog', default=True,
772 )
772 )
773 coreconfigitem(
773 coreconfigitem(
774 b'format',
774 b'format',
775 b'revlog-compression',
775 b'revlog-compression',
776 default=lambda: [b'zlib'],
776 default=lambda: [b'zlib'],
777 alias=[(b'experimental', b'format.compression')],
777 alias=[(b'experimental', b'format.compression')],
778 )
778 )
779 coreconfigitem(
779 coreconfigitem(
780 b'format', b'usefncache', default=True,
780 b'format', b'usefncache', default=True,
781 )
781 )
782 coreconfigitem(
782 coreconfigitem(
783 b'format', b'usegeneraldelta', default=True,
783 b'format', b'usegeneraldelta', default=True,
784 )
784 )
785 coreconfigitem(
785 coreconfigitem(
786 b'format', b'usestore', default=True,
786 b'format', b'usestore', default=True,
787 )
787 )
788 coreconfigitem(
788 coreconfigitem(
789 b'format',
789 b'format',
790 b'exp-use-copies-side-data-changeset',
790 b'exp-use-copies-side-data-changeset',
791 default=False,
791 default=False,
792 experimental=True,
792 experimental=True,
793 )
793 )
794 coreconfigitem(
794 coreconfigitem(
795 b'format', b'exp-use-side-data', default=False, experimental=True,
795 b'format', b'exp-use-side-data', default=False, experimental=True,
796 )
796 )
797 coreconfigitem(
797 coreconfigitem(
798 b'format', b'internal-phase', default=False, experimental=True,
798 b'format', b'internal-phase', default=False, experimental=True,
799 )
799 )
800 coreconfigitem(
800 coreconfigitem(
801 b'fsmonitor', b'warn_when_unused', default=True,
801 b'fsmonitor', b'warn_when_unused', default=True,
802 )
802 )
803 coreconfigitem(
803 coreconfigitem(
804 b'fsmonitor', b'warn_update_file_count', default=50000,
804 b'fsmonitor', b'warn_update_file_count', default=50000,
805 )
805 )
806 coreconfigitem(
806 coreconfigitem(
807 b'help', br'hidden-command\..*', default=False, generic=True,
807 b'help', br'hidden-command\..*', default=False, generic=True,
808 )
808 )
809 coreconfigitem(
809 coreconfigitem(
810 b'help', br'hidden-topic\..*', default=False, generic=True,
810 b'help', br'hidden-topic\..*', default=False, generic=True,
811 )
811 )
812 coreconfigitem(
812 coreconfigitem(
813 b'hooks', b'.*', default=dynamicdefault, generic=True,
813 b'hooks', b'.*', default=dynamicdefault, generic=True,
814 )
814 )
815 coreconfigitem(
815 coreconfigitem(
816 b'hgweb-paths', b'.*', default=list, generic=True,
816 b'hgweb-paths', b'.*', default=list, generic=True,
817 )
817 )
818 coreconfigitem(
818 coreconfigitem(
819 b'hostfingerprints', b'.*', default=list, generic=True,
819 b'hostfingerprints', b'.*', default=list, generic=True,
820 )
820 )
821 coreconfigitem(
821 coreconfigitem(
822 b'hostsecurity', b'ciphers', default=None,
822 b'hostsecurity', b'ciphers', default=None,
823 )
823 )
824 coreconfigitem(
824 coreconfigitem(
825 b'hostsecurity', b'disabletls10warning', default=False,
825 b'hostsecurity', b'disabletls10warning', default=False,
826 )
826 )
827 coreconfigitem(
827 coreconfigitem(
828 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
828 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
829 )
829 )
830 coreconfigitem(
830 coreconfigitem(
831 b'hostsecurity',
831 b'hostsecurity',
832 b'.*:minimumprotocol$',
832 b'.*:minimumprotocol$',
833 default=dynamicdefault,
833 default=dynamicdefault,
834 generic=True,
834 generic=True,
835 )
835 )
836 coreconfigitem(
836 coreconfigitem(
837 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
837 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
838 )
838 )
839 coreconfigitem(
839 coreconfigitem(
840 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
840 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
841 )
841 )
842 coreconfigitem(
842 coreconfigitem(
843 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
843 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
844 )
844 )
845
845
846 coreconfigitem(
846 coreconfigitem(
847 b'http_proxy', b'always', default=False,
847 b'http_proxy', b'always', default=False,
848 )
848 )
849 coreconfigitem(
849 coreconfigitem(
850 b'http_proxy', b'host', default=None,
850 b'http_proxy', b'host', default=None,
851 )
851 )
852 coreconfigitem(
852 coreconfigitem(
853 b'http_proxy', b'no', default=list,
853 b'http_proxy', b'no', default=list,
854 )
854 )
855 coreconfigitem(
855 coreconfigitem(
856 b'http_proxy', b'passwd', default=None,
856 b'http_proxy', b'passwd', default=None,
857 )
857 )
858 coreconfigitem(
858 coreconfigitem(
859 b'http_proxy', b'user', default=None,
859 b'http_proxy', b'user', default=None,
860 )
860 )
861
861
862 coreconfigitem(
862 coreconfigitem(
863 b'http', b'timeout', default=None,
863 b'http', b'timeout', default=None,
864 )
864 )
865
865
866 coreconfigitem(
866 coreconfigitem(
867 b'logtoprocess', b'commandexception', default=None,
867 b'logtoprocess', b'commandexception', default=None,
868 )
868 )
869 coreconfigitem(
869 coreconfigitem(
870 b'logtoprocess', b'commandfinish', default=None,
870 b'logtoprocess', b'commandfinish', default=None,
871 )
871 )
872 coreconfigitem(
872 coreconfigitem(
873 b'logtoprocess', b'command', default=None,
873 b'logtoprocess', b'command', default=None,
874 )
874 )
875 coreconfigitem(
875 coreconfigitem(
876 b'logtoprocess', b'develwarn', default=None,
876 b'logtoprocess', b'develwarn', default=None,
877 )
877 )
878 coreconfigitem(
878 coreconfigitem(
879 b'logtoprocess', b'uiblocked', default=None,
879 b'logtoprocess', b'uiblocked', default=None,
880 )
880 )
881 coreconfigitem(
881 coreconfigitem(
882 b'merge', b'checkunknown', default=b'abort',
882 b'merge', b'checkunknown', default=b'abort',
883 )
883 )
884 coreconfigitem(
884 coreconfigitem(
885 b'merge', b'checkignored', default=b'abort',
885 b'merge', b'checkignored', default=b'abort',
886 )
886 )
887 coreconfigitem(
887 coreconfigitem(
888 b'experimental', b'merge.checkpathconflicts', default=False,
888 b'experimental', b'merge.checkpathconflicts', default=False,
889 )
889 )
890 coreconfigitem(
890 coreconfigitem(
891 b'merge', b'followcopies', default=True,
891 b'merge', b'followcopies', default=True,
892 )
892 )
893 coreconfigitem(
893 coreconfigitem(
894 b'merge', b'on-failure', default=b'continue',
894 b'merge', b'on-failure', default=b'continue',
895 )
895 )
896 coreconfigitem(
896 coreconfigitem(
897 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
897 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
898 )
898 )
899 coreconfigitem(
899 coreconfigitem(
900 b'merge', b'strict-capability-check', default=False,
900 b'merge', b'strict-capability-check', default=False,
901 )
901 )
902 coreconfigitem(
902 coreconfigitem(
903 b'merge-tools', b'.*', default=None, generic=True,
903 b'merge-tools', b'.*', default=None, generic=True,
904 )
904 )
905 coreconfigitem(
905 coreconfigitem(
906 b'merge-tools',
906 b'merge-tools',
907 br'.*\.args$',
907 br'.*\.args$',
908 default=b"$local $base $other",
908 default=b"$local $base $other",
909 generic=True,
909 generic=True,
910 priority=-1,
910 priority=-1,
911 )
911 )
912 coreconfigitem(
912 coreconfigitem(
913 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
913 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
914 )
914 )
915 coreconfigitem(
915 coreconfigitem(
916 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
916 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
917 )
917 )
918 coreconfigitem(
918 coreconfigitem(
919 b'merge-tools',
919 b'merge-tools',
920 br'.*\.checkchanged$',
920 br'.*\.checkchanged$',
921 default=False,
921 default=False,
922 generic=True,
922 generic=True,
923 priority=-1,
923 priority=-1,
924 )
924 )
925 coreconfigitem(
925 coreconfigitem(
926 b'merge-tools',
926 b'merge-tools',
927 br'.*\.executable$',
927 br'.*\.executable$',
928 default=dynamicdefault,
928 default=dynamicdefault,
929 generic=True,
929 generic=True,
930 priority=-1,
930 priority=-1,
931 )
931 )
932 coreconfigitem(
932 coreconfigitem(
933 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
933 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
934 )
934 )
935 coreconfigitem(
935 coreconfigitem(
936 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
936 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
937 )
937 )
938 coreconfigitem(
938 coreconfigitem(
939 b'merge-tools',
939 b'merge-tools',
940 br'.*\.mergemarkers$',
940 br'.*\.mergemarkers$',
941 default=b'basic',
941 default=b'basic',
942 generic=True,
942 generic=True,
943 priority=-1,
943 priority=-1,
944 )
944 )
945 coreconfigitem(
945 coreconfigitem(
946 b'merge-tools',
946 b'merge-tools',
947 br'.*\.mergemarkertemplate$',
947 br'.*\.mergemarkertemplate$',
948 default=dynamicdefault, # take from ui.mergemarkertemplate
948 default=dynamicdefault, # take from ui.mergemarkertemplate
949 generic=True,
949 generic=True,
950 priority=-1,
950 priority=-1,
951 )
951 )
952 coreconfigitem(
952 coreconfigitem(
953 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
953 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
954 )
954 )
955 coreconfigitem(
955 coreconfigitem(
956 b'merge-tools',
956 b'merge-tools',
957 br'.*\.premerge$',
957 br'.*\.premerge$',
958 default=dynamicdefault,
958 default=dynamicdefault,
959 generic=True,
959 generic=True,
960 priority=-1,
960 priority=-1,
961 )
961 )
962 coreconfigitem(
962 coreconfigitem(
963 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
963 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
964 )
964 )
965 coreconfigitem(
965 coreconfigitem(
966 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
966 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
967 )
967 )
968 coreconfigitem(
968 coreconfigitem(
969 b'pager', b'ignore', default=list,
969 b'pager', b'ignore', default=list,
970 )
970 )
971 coreconfigitem(
971 coreconfigitem(
972 b'pager', b'pager', default=dynamicdefault,
972 b'pager', b'pager', default=dynamicdefault,
973 )
973 )
974 coreconfigitem(
974 coreconfigitem(
975 b'patch', b'eol', default=b'strict',
975 b'patch', b'eol', default=b'strict',
976 )
976 )
977 coreconfigitem(
977 coreconfigitem(
978 b'patch', b'fuzz', default=2,
978 b'patch', b'fuzz', default=2,
979 )
979 )
980 coreconfigitem(
980 coreconfigitem(
981 b'paths', b'default', default=None,
981 b'paths', b'default', default=None,
982 )
982 )
983 coreconfigitem(
983 coreconfigitem(
984 b'paths', b'default-push', default=None,
984 b'paths', b'default-push', default=None,
985 )
985 )
986 coreconfigitem(
986 coreconfigitem(
987 b'paths', b'.*', default=None, generic=True,
987 b'paths', b'.*', default=None, generic=True,
988 )
988 )
989 coreconfigitem(
989 coreconfigitem(
990 b'phases', b'checksubrepos', default=b'follow',
990 b'phases', b'checksubrepos', default=b'follow',
991 )
991 )
992 coreconfigitem(
992 coreconfigitem(
993 b'phases', b'new-commit', default=b'draft',
993 b'phases', b'new-commit', default=b'draft',
994 )
994 )
995 coreconfigitem(
995 coreconfigitem(
996 b'phases', b'publish', default=True,
996 b'phases', b'publish', default=True,
997 )
997 )
998 coreconfigitem(
998 coreconfigitem(
999 b'profiling', b'enabled', default=False,
999 b'profiling', b'enabled', default=False,
1000 )
1000 )
1001 coreconfigitem(
1001 coreconfigitem(
1002 b'profiling', b'format', default=b'text',
1002 b'profiling', b'format', default=b'text',
1003 )
1003 )
1004 coreconfigitem(
1004 coreconfigitem(
1005 b'profiling', b'freq', default=1000,
1005 b'profiling', b'freq', default=1000,
1006 )
1006 )
1007 coreconfigitem(
1007 coreconfigitem(
1008 b'profiling', b'limit', default=30,
1008 b'profiling', b'limit', default=30,
1009 )
1009 )
1010 coreconfigitem(
1010 coreconfigitem(
1011 b'profiling', b'nested', default=0,
1011 b'profiling', b'nested', default=0,
1012 )
1012 )
1013 coreconfigitem(
1013 coreconfigitem(
1014 b'profiling', b'output', default=None,
1014 b'profiling', b'output', default=None,
1015 )
1015 )
1016 coreconfigitem(
1016 coreconfigitem(
1017 b'profiling', b'showmax', default=0.999,
1017 b'profiling', b'showmax', default=0.999,
1018 )
1018 )
1019 coreconfigitem(
1019 coreconfigitem(
1020 b'profiling', b'showmin', default=dynamicdefault,
1020 b'profiling', b'showmin', default=dynamicdefault,
1021 )
1021 )
1022 coreconfigitem(
1022 coreconfigitem(
1023 b'profiling', b'showtime', default=True,
1023 b'profiling', b'showtime', default=True,
1024 )
1024 )
1025 coreconfigitem(
1025 coreconfigitem(
1026 b'profiling', b'sort', default=b'inlinetime',
1026 b'profiling', b'sort', default=b'inlinetime',
1027 )
1027 )
1028 coreconfigitem(
1028 coreconfigitem(
1029 b'profiling', b'statformat', default=b'hotpath',
1029 b'profiling', b'statformat', default=b'hotpath',
1030 )
1030 )
1031 coreconfigitem(
1031 coreconfigitem(
1032 b'profiling', b'time-track', default=dynamicdefault,
1032 b'profiling', b'time-track', default=dynamicdefault,
1033 )
1033 )
1034 coreconfigitem(
1034 coreconfigitem(
1035 b'profiling', b'type', default=b'stat',
1035 b'profiling', b'type', default=b'stat',
1036 )
1036 )
1037 coreconfigitem(
1037 coreconfigitem(
1038 b'progress', b'assume-tty', default=False,
1038 b'progress', b'assume-tty', default=False,
1039 )
1039 )
1040 coreconfigitem(
1040 coreconfigitem(
1041 b'progress', b'changedelay', default=1,
1041 b'progress', b'changedelay', default=1,
1042 )
1042 )
1043 coreconfigitem(
1043 coreconfigitem(
1044 b'progress', b'clear-complete', default=True,
1044 b'progress', b'clear-complete', default=True,
1045 )
1045 )
1046 coreconfigitem(
1046 coreconfigitem(
1047 b'progress', b'debug', default=False,
1047 b'progress', b'debug', default=False,
1048 )
1048 )
1049 coreconfigitem(
1049 coreconfigitem(
1050 b'progress', b'delay', default=3,
1050 b'progress', b'delay', default=3,
1051 )
1051 )
1052 coreconfigitem(
1052 coreconfigitem(
1053 b'progress', b'disable', default=False,
1053 b'progress', b'disable', default=False,
1054 )
1054 )
1055 coreconfigitem(
1055 coreconfigitem(
1056 b'progress', b'estimateinterval', default=60.0,
1056 b'progress', b'estimateinterval', default=60.0,
1057 )
1057 )
1058 coreconfigitem(
1058 coreconfigitem(
1059 b'progress',
1059 b'progress',
1060 b'format',
1060 b'format',
1061 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1061 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1062 )
1062 )
1063 coreconfigitem(
1063 coreconfigitem(
1064 b'progress', b'refresh', default=0.1,
1064 b'progress', b'refresh', default=0.1,
1065 )
1065 )
1066 coreconfigitem(
1066 coreconfigitem(
1067 b'progress', b'width', default=dynamicdefault,
1067 b'progress', b'width', default=dynamicdefault,
1068 )
1068 )
1069 coreconfigitem(
1069 coreconfigitem(
1070 b'pull', b'confirm', default=False,
1071 )
1072 coreconfigitem(
1070 b'push', b'pushvars.server', default=False,
1073 b'push', b'pushvars.server', default=False,
1071 )
1074 )
1072 coreconfigitem(
1075 coreconfigitem(
1073 b'rewrite',
1076 b'rewrite',
1074 b'backup-bundle',
1077 b'backup-bundle',
1075 default=True,
1078 default=True,
1076 alias=[(b'ui', b'history-editing-backup')],
1079 alias=[(b'ui', b'history-editing-backup')],
1077 )
1080 )
1078 coreconfigitem(
1081 coreconfigitem(
1079 b'rewrite', b'update-timestamp', default=False,
1082 b'rewrite', b'update-timestamp', default=False,
1080 )
1083 )
1081 coreconfigitem(
1084 coreconfigitem(
1082 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1085 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1083 )
1086 )
1084 coreconfigitem(
1087 coreconfigitem(
1085 b'storage',
1088 b'storage',
1086 b'revlog.optimize-delta-parent-choice',
1089 b'revlog.optimize-delta-parent-choice',
1087 default=True,
1090 default=True,
1088 alias=[(b'format', b'aggressivemergedeltas')],
1091 alias=[(b'format', b'aggressivemergedeltas')],
1089 )
1092 )
1090 coreconfigitem(
1093 coreconfigitem(
1091 b'storage', b'revlog.reuse-external-delta', default=True,
1094 b'storage', b'revlog.reuse-external-delta', default=True,
1092 )
1095 )
1093 coreconfigitem(
1096 coreconfigitem(
1094 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1097 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1095 )
1098 )
1096 coreconfigitem(
1099 coreconfigitem(
1097 b'storage', b'revlog.zlib.level', default=None,
1100 b'storage', b'revlog.zlib.level', default=None,
1098 )
1101 )
1099 coreconfigitem(
1102 coreconfigitem(
1100 b'storage', b'revlog.zstd.level', default=None,
1103 b'storage', b'revlog.zstd.level', default=None,
1101 )
1104 )
1102 coreconfigitem(
1105 coreconfigitem(
1103 b'server', b'bookmarks-pushkey-compat', default=True,
1106 b'server', b'bookmarks-pushkey-compat', default=True,
1104 )
1107 )
1105 coreconfigitem(
1108 coreconfigitem(
1106 b'server', b'bundle1', default=True,
1109 b'server', b'bundle1', default=True,
1107 )
1110 )
1108 coreconfigitem(
1111 coreconfigitem(
1109 b'server', b'bundle1gd', default=None,
1112 b'server', b'bundle1gd', default=None,
1110 )
1113 )
1111 coreconfigitem(
1114 coreconfigitem(
1112 b'server', b'bundle1.pull', default=None,
1115 b'server', b'bundle1.pull', default=None,
1113 )
1116 )
1114 coreconfigitem(
1117 coreconfigitem(
1115 b'server', b'bundle1gd.pull', default=None,
1118 b'server', b'bundle1gd.pull', default=None,
1116 )
1119 )
1117 coreconfigitem(
1120 coreconfigitem(
1118 b'server', b'bundle1.push', default=None,
1121 b'server', b'bundle1.push', default=None,
1119 )
1122 )
1120 coreconfigitem(
1123 coreconfigitem(
1121 b'server', b'bundle1gd.push', default=None,
1124 b'server', b'bundle1gd.push', default=None,
1122 )
1125 )
1123 coreconfigitem(
1126 coreconfigitem(
1124 b'server',
1127 b'server',
1125 b'bundle2.stream',
1128 b'bundle2.stream',
1126 default=True,
1129 default=True,
1127 alias=[(b'experimental', b'bundle2.stream')],
1130 alias=[(b'experimental', b'bundle2.stream')],
1128 )
1131 )
1129 coreconfigitem(
1132 coreconfigitem(
1130 b'server', b'compressionengines', default=list,
1133 b'server', b'compressionengines', default=list,
1131 )
1134 )
1132 coreconfigitem(
1135 coreconfigitem(
1133 b'server', b'concurrent-push-mode', default=b'check-related',
1136 b'server', b'concurrent-push-mode', default=b'check-related',
1134 )
1137 )
1135 coreconfigitem(
1138 coreconfigitem(
1136 b'server', b'disablefullbundle', default=False,
1139 b'server', b'disablefullbundle', default=False,
1137 )
1140 )
1138 coreconfigitem(
1141 coreconfigitem(
1139 b'server', b'maxhttpheaderlen', default=1024,
1142 b'server', b'maxhttpheaderlen', default=1024,
1140 )
1143 )
1141 coreconfigitem(
1144 coreconfigitem(
1142 b'server', b'pullbundle', default=False,
1145 b'server', b'pullbundle', default=False,
1143 )
1146 )
1144 coreconfigitem(
1147 coreconfigitem(
1145 b'server', b'preferuncompressed', default=False,
1148 b'server', b'preferuncompressed', default=False,
1146 )
1149 )
1147 coreconfigitem(
1150 coreconfigitem(
1148 b'server', b'streamunbundle', default=False,
1151 b'server', b'streamunbundle', default=False,
1149 )
1152 )
1150 coreconfigitem(
1153 coreconfigitem(
1151 b'server', b'uncompressed', default=True,
1154 b'server', b'uncompressed', default=True,
1152 )
1155 )
1153 coreconfigitem(
1156 coreconfigitem(
1154 b'server', b'uncompressedallowsecret', default=False,
1157 b'server', b'uncompressedallowsecret', default=False,
1155 )
1158 )
1156 coreconfigitem(
1159 coreconfigitem(
1157 b'server', b'view', default=b'served',
1160 b'server', b'view', default=b'served',
1158 )
1161 )
1159 coreconfigitem(
1162 coreconfigitem(
1160 b'server', b'validate', default=False,
1163 b'server', b'validate', default=False,
1161 )
1164 )
1162 coreconfigitem(
1165 coreconfigitem(
1163 b'server', b'zliblevel', default=-1,
1166 b'server', b'zliblevel', default=-1,
1164 )
1167 )
1165 coreconfigitem(
1168 coreconfigitem(
1166 b'server', b'zstdlevel', default=3,
1169 b'server', b'zstdlevel', default=3,
1167 )
1170 )
1168 coreconfigitem(
1171 coreconfigitem(
1169 b'share', b'pool', default=None,
1172 b'share', b'pool', default=None,
1170 )
1173 )
1171 coreconfigitem(
1174 coreconfigitem(
1172 b'share', b'poolnaming', default=b'identity',
1175 b'share', b'poolnaming', default=b'identity',
1173 )
1176 )
1174 coreconfigitem(
1177 coreconfigitem(
1175 b'shelve', b'maxbackups', default=10,
1178 b'shelve', b'maxbackups', default=10,
1176 )
1179 )
1177 coreconfigitem(
1180 coreconfigitem(
1178 b'smtp', b'host', default=None,
1181 b'smtp', b'host', default=None,
1179 )
1182 )
1180 coreconfigitem(
1183 coreconfigitem(
1181 b'smtp', b'local_hostname', default=None,
1184 b'smtp', b'local_hostname', default=None,
1182 )
1185 )
1183 coreconfigitem(
1186 coreconfigitem(
1184 b'smtp', b'password', default=None,
1187 b'smtp', b'password', default=None,
1185 )
1188 )
1186 coreconfigitem(
1189 coreconfigitem(
1187 b'smtp', b'port', default=dynamicdefault,
1190 b'smtp', b'port', default=dynamicdefault,
1188 )
1191 )
1189 coreconfigitem(
1192 coreconfigitem(
1190 b'smtp', b'tls', default=b'none',
1193 b'smtp', b'tls', default=b'none',
1191 )
1194 )
1192 coreconfigitem(
1195 coreconfigitem(
1193 b'smtp', b'username', default=None,
1196 b'smtp', b'username', default=None,
1194 )
1197 )
1195 coreconfigitem(
1198 coreconfigitem(
1196 b'sparse', b'missingwarning', default=True, experimental=True,
1199 b'sparse', b'missingwarning', default=True, experimental=True,
1197 )
1200 )
1198 coreconfigitem(
1201 coreconfigitem(
1199 b'subrepos',
1202 b'subrepos',
1200 b'allowed',
1203 b'allowed',
1201 default=dynamicdefault, # to make backporting simpler
1204 default=dynamicdefault, # to make backporting simpler
1202 )
1205 )
1203 coreconfigitem(
1206 coreconfigitem(
1204 b'subrepos', b'hg:allowed', default=dynamicdefault,
1207 b'subrepos', b'hg:allowed', default=dynamicdefault,
1205 )
1208 )
1206 coreconfigitem(
1209 coreconfigitem(
1207 b'subrepos', b'git:allowed', default=dynamicdefault,
1210 b'subrepos', b'git:allowed', default=dynamicdefault,
1208 )
1211 )
1209 coreconfigitem(
1212 coreconfigitem(
1210 b'subrepos', b'svn:allowed', default=dynamicdefault,
1213 b'subrepos', b'svn:allowed', default=dynamicdefault,
1211 )
1214 )
1212 coreconfigitem(
1215 coreconfigitem(
1213 b'templates', b'.*', default=None, generic=True,
1216 b'templates', b'.*', default=None, generic=True,
1214 )
1217 )
1215 coreconfigitem(
1218 coreconfigitem(
1216 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1219 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1217 )
1220 )
1218 coreconfigitem(
1221 coreconfigitem(
1219 b'trusted', b'groups', default=list,
1222 b'trusted', b'groups', default=list,
1220 )
1223 )
1221 coreconfigitem(
1224 coreconfigitem(
1222 b'trusted', b'users', default=list,
1225 b'trusted', b'users', default=list,
1223 )
1226 )
1224 coreconfigitem(
1227 coreconfigitem(
1225 b'ui', b'_usedassubrepo', default=False,
1228 b'ui', b'_usedassubrepo', default=False,
1226 )
1229 )
1227 coreconfigitem(
1230 coreconfigitem(
1228 b'ui', b'allowemptycommit', default=False,
1231 b'ui', b'allowemptycommit', default=False,
1229 )
1232 )
1230 coreconfigitem(
1233 coreconfigitem(
1231 b'ui', b'archivemeta', default=True,
1234 b'ui', b'archivemeta', default=True,
1232 )
1235 )
1233 coreconfigitem(
1236 coreconfigitem(
1234 b'ui', b'askusername', default=False,
1237 b'ui', b'askusername', default=False,
1235 )
1238 )
1236 coreconfigitem(
1239 coreconfigitem(
1237 b'ui', b'clonebundlefallback', default=False,
1240 b'ui', b'clonebundlefallback', default=False,
1238 )
1241 )
1239 coreconfigitem(
1242 coreconfigitem(
1240 b'ui', b'clonebundleprefers', default=list,
1243 b'ui', b'clonebundleprefers', default=list,
1241 )
1244 )
1242 coreconfigitem(
1245 coreconfigitem(
1243 b'ui', b'clonebundles', default=True,
1246 b'ui', b'clonebundles', default=True,
1244 )
1247 )
1245 coreconfigitem(
1248 coreconfigitem(
1246 b'ui', b'color', default=b'auto',
1249 b'ui', b'color', default=b'auto',
1247 )
1250 )
1248 coreconfigitem(
1251 coreconfigitem(
1249 b'ui', b'commitsubrepos', default=False,
1252 b'ui', b'commitsubrepos', default=False,
1250 )
1253 )
1251 coreconfigitem(
1254 coreconfigitem(
1252 b'ui', b'debug', default=False,
1255 b'ui', b'debug', default=False,
1253 )
1256 )
1254 coreconfigitem(
1257 coreconfigitem(
1255 b'ui', b'debugger', default=None,
1258 b'ui', b'debugger', default=None,
1256 )
1259 )
1257 coreconfigitem(
1260 coreconfigitem(
1258 b'ui', b'editor', default=dynamicdefault,
1261 b'ui', b'editor', default=dynamicdefault,
1259 )
1262 )
1260 coreconfigitem(
1263 coreconfigitem(
1261 b'ui', b'fallbackencoding', default=None,
1264 b'ui', b'fallbackencoding', default=None,
1262 )
1265 )
1263 coreconfigitem(
1266 coreconfigitem(
1264 b'ui', b'forcecwd', default=None,
1267 b'ui', b'forcecwd', default=None,
1265 )
1268 )
1266 coreconfigitem(
1269 coreconfigitem(
1267 b'ui', b'forcemerge', default=None,
1270 b'ui', b'forcemerge', default=None,
1268 )
1271 )
1269 coreconfigitem(
1272 coreconfigitem(
1270 b'ui', b'formatdebug', default=False,
1273 b'ui', b'formatdebug', default=False,
1271 )
1274 )
1272 coreconfigitem(
1275 coreconfigitem(
1273 b'ui', b'formatjson', default=False,
1276 b'ui', b'formatjson', default=False,
1274 )
1277 )
1275 coreconfigitem(
1278 coreconfigitem(
1276 b'ui', b'formatted', default=None,
1279 b'ui', b'formatted', default=None,
1277 )
1280 )
1278 coreconfigitem(
1281 coreconfigitem(
1279 b'ui', b'graphnodetemplate', default=None,
1282 b'ui', b'graphnodetemplate', default=None,
1280 )
1283 )
1281 coreconfigitem(
1284 coreconfigitem(
1282 b'ui', b'interactive', default=None,
1285 b'ui', b'interactive', default=None,
1283 )
1286 )
1284 coreconfigitem(
1287 coreconfigitem(
1285 b'ui', b'interface', default=None,
1288 b'ui', b'interface', default=None,
1286 )
1289 )
1287 coreconfigitem(
1290 coreconfigitem(
1288 b'ui', b'interface.chunkselector', default=None,
1291 b'ui', b'interface.chunkselector', default=None,
1289 )
1292 )
1290 coreconfigitem(
1293 coreconfigitem(
1291 b'ui', b'large-file-limit', default=10000000,
1294 b'ui', b'large-file-limit', default=10000000,
1292 )
1295 )
1293 coreconfigitem(
1296 coreconfigitem(
1294 b'ui', b'logblockedtimes', default=False,
1297 b'ui', b'logblockedtimes', default=False,
1295 )
1298 )
1296 coreconfigitem(
1299 coreconfigitem(
1297 b'ui', b'logtemplate', default=None,
1300 b'ui', b'logtemplate', default=None,
1298 )
1301 )
1299 coreconfigitem(
1302 coreconfigitem(
1300 b'ui', b'merge', default=None,
1303 b'ui', b'merge', default=None,
1301 )
1304 )
1302 coreconfigitem(
1305 coreconfigitem(
1303 b'ui', b'mergemarkers', default=b'basic',
1306 b'ui', b'mergemarkers', default=b'basic',
1304 )
1307 )
1305 coreconfigitem(
1308 coreconfigitem(
1306 b'ui',
1309 b'ui',
1307 b'mergemarkertemplate',
1310 b'mergemarkertemplate',
1308 default=(
1311 default=(
1309 b'{node|short} '
1312 b'{node|short} '
1310 b'{ifeq(tags, "tip", "", '
1313 b'{ifeq(tags, "tip", "", '
1311 b'ifeq(tags, "", "", "{tags} "))}'
1314 b'ifeq(tags, "", "", "{tags} "))}'
1312 b'{if(bookmarks, "{bookmarks} ")}'
1315 b'{if(bookmarks, "{bookmarks} ")}'
1313 b'{ifeq(branch, "default", "", "{branch} ")}'
1316 b'{ifeq(branch, "default", "", "{branch} ")}'
1314 b'- {author|user}: {desc|firstline}'
1317 b'- {author|user}: {desc|firstline}'
1315 ),
1318 ),
1316 )
1319 )
1317 coreconfigitem(
1320 coreconfigitem(
1318 b'ui', b'message-output', default=b'stdio',
1321 b'ui', b'message-output', default=b'stdio',
1319 )
1322 )
1320 coreconfigitem(
1323 coreconfigitem(
1321 b'ui', b'nontty', default=False,
1324 b'ui', b'nontty', default=False,
1322 )
1325 )
1323 coreconfigitem(
1326 coreconfigitem(
1324 b'ui', b'origbackuppath', default=None,
1327 b'ui', b'origbackuppath', default=None,
1325 )
1328 )
1326 coreconfigitem(
1329 coreconfigitem(
1327 b'ui', b'paginate', default=True,
1330 b'ui', b'paginate', default=True,
1328 )
1331 )
1329 coreconfigitem(
1332 coreconfigitem(
1330 b'ui', b'patch', default=None,
1333 b'ui', b'patch', default=None,
1331 )
1334 )
1332 coreconfigitem(
1335 coreconfigitem(
1333 b'ui', b'pre-merge-tool-output-template', default=None,
1336 b'ui', b'pre-merge-tool-output-template', default=None,
1334 )
1337 )
1335 coreconfigitem(
1338 coreconfigitem(
1336 b'ui', b'portablefilenames', default=b'warn',
1339 b'ui', b'portablefilenames', default=b'warn',
1337 )
1340 )
1338 coreconfigitem(
1341 coreconfigitem(
1339 b'ui', b'promptecho', default=False,
1342 b'ui', b'promptecho', default=False,
1340 )
1343 )
1341 coreconfigitem(
1344 coreconfigitem(
1342 b'ui', b'quiet', default=False,
1345 b'ui', b'quiet', default=False,
1343 )
1346 )
1344 coreconfigitem(
1347 coreconfigitem(
1345 b'ui', b'quietbookmarkmove', default=False,
1348 b'ui', b'quietbookmarkmove', default=False,
1346 )
1349 )
1347 coreconfigitem(
1350 coreconfigitem(
1348 b'ui', b'relative-paths', default=b'legacy',
1351 b'ui', b'relative-paths', default=b'legacy',
1349 )
1352 )
1350 coreconfigitem(
1353 coreconfigitem(
1351 b'ui', b'remotecmd', default=b'hg',
1354 b'ui', b'remotecmd', default=b'hg',
1352 )
1355 )
1353 coreconfigitem(
1356 coreconfigitem(
1354 b'ui', b'report_untrusted', default=True,
1357 b'ui', b'report_untrusted', default=True,
1355 )
1358 )
1356 coreconfigitem(
1359 coreconfigitem(
1357 b'ui', b'rollback', default=True,
1360 b'ui', b'rollback', default=True,
1358 )
1361 )
1359 coreconfigitem(
1362 coreconfigitem(
1360 b'ui', b'signal-safe-lock', default=True,
1363 b'ui', b'signal-safe-lock', default=True,
1361 )
1364 )
1362 coreconfigitem(
1365 coreconfigitem(
1363 b'ui', b'slash', default=False,
1366 b'ui', b'slash', default=False,
1364 )
1367 )
1365 coreconfigitem(
1368 coreconfigitem(
1366 b'ui', b'ssh', default=b'ssh',
1369 b'ui', b'ssh', default=b'ssh',
1367 )
1370 )
1368 coreconfigitem(
1371 coreconfigitem(
1369 b'ui', b'ssherrorhint', default=None,
1372 b'ui', b'ssherrorhint', default=None,
1370 )
1373 )
1371 coreconfigitem(
1374 coreconfigitem(
1372 b'ui', b'statuscopies', default=False,
1375 b'ui', b'statuscopies', default=False,
1373 )
1376 )
1374 coreconfigitem(
1377 coreconfigitem(
1375 b'ui', b'strict', default=False,
1378 b'ui', b'strict', default=False,
1376 )
1379 )
1377 coreconfigitem(
1380 coreconfigitem(
1378 b'ui', b'style', default=b'',
1381 b'ui', b'style', default=b'',
1379 )
1382 )
1380 coreconfigitem(
1383 coreconfigitem(
1381 b'ui', b'supportcontact', default=None,
1384 b'ui', b'supportcontact', default=None,
1382 )
1385 )
1383 coreconfigitem(
1386 coreconfigitem(
1384 b'ui', b'textwidth', default=78,
1387 b'ui', b'textwidth', default=78,
1385 )
1388 )
1386 coreconfigitem(
1389 coreconfigitem(
1387 b'ui', b'timeout', default=b'600',
1390 b'ui', b'timeout', default=b'600',
1388 )
1391 )
1389 coreconfigitem(
1392 coreconfigitem(
1390 b'ui', b'timeout.warn', default=0,
1393 b'ui', b'timeout.warn', default=0,
1391 )
1394 )
1392 coreconfigitem(
1395 coreconfigitem(
1393 b'ui', b'traceback', default=False,
1396 b'ui', b'traceback', default=False,
1394 )
1397 )
1395 coreconfigitem(
1398 coreconfigitem(
1396 b'ui', b'tweakdefaults', default=False,
1399 b'ui', b'tweakdefaults', default=False,
1397 )
1400 )
1398 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1401 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1399 coreconfigitem(
1402 coreconfigitem(
1400 b'ui', b'verbose', default=False,
1403 b'ui', b'verbose', default=False,
1401 )
1404 )
1402 coreconfigitem(
1405 coreconfigitem(
1403 b'verify', b'skipflags', default=None,
1406 b'verify', b'skipflags', default=None,
1404 )
1407 )
1405 coreconfigitem(
1408 coreconfigitem(
1406 b'web', b'allowbz2', default=False,
1409 b'web', b'allowbz2', default=False,
1407 )
1410 )
1408 coreconfigitem(
1411 coreconfigitem(
1409 b'web', b'allowgz', default=False,
1412 b'web', b'allowgz', default=False,
1410 )
1413 )
1411 coreconfigitem(
1414 coreconfigitem(
1412 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1415 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1413 )
1416 )
1414 coreconfigitem(
1417 coreconfigitem(
1415 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1418 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1416 )
1419 )
1417 coreconfigitem(
1420 coreconfigitem(
1418 b'web', b'allowzip', default=False,
1421 b'web', b'allowzip', default=False,
1419 )
1422 )
1420 coreconfigitem(
1423 coreconfigitem(
1421 b'web', b'archivesubrepos', default=False,
1424 b'web', b'archivesubrepos', default=False,
1422 )
1425 )
1423 coreconfigitem(
1426 coreconfigitem(
1424 b'web', b'cache', default=True,
1427 b'web', b'cache', default=True,
1425 )
1428 )
1426 coreconfigitem(
1429 coreconfigitem(
1427 b'web', b'comparisoncontext', default=5,
1430 b'web', b'comparisoncontext', default=5,
1428 )
1431 )
1429 coreconfigitem(
1432 coreconfigitem(
1430 b'web', b'contact', default=None,
1433 b'web', b'contact', default=None,
1431 )
1434 )
1432 coreconfigitem(
1435 coreconfigitem(
1433 b'web', b'deny_push', default=list,
1436 b'web', b'deny_push', default=list,
1434 )
1437 )
1435 coreconfigitem(
1438 coreconfigitem(
1436 b'web', b'guessmime', default=False,
1439 b'web', b'guessmime', default=False,
1437 )
1440 )
1438 coreconfigitem(
1441 coreconfigitem(
1439 b'web', b'hidden', default=False,
1442 b'web', b'hidden', default=False,
1440 )
1443 )
1441 coreconfigitem(
1444 coreconfigitem(
1442 b'web', b'labels', default=list,
1445 b'web', b'labels', default=list,
1443 )
1446 )
1444 coreconfigitem(
1447 coreconfigitem(
1445 b'web', b'logoimg', default=b'hglogo.png',
1448 b'web', b'logoimg', default=b'hglogo.png',
1446 )
1449 )
1447 coreconfigitem(
1450 coreconfigitem(
1448 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1451 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1449 )
1452 )
1450 coreconfigitem(
1453 coreconfigitem(
1451 b'web', b'accesslog', default=b'-',
1454 b'web', b'accesslog', default=b'-',
1452 )
1455 )
1453 coreconfigitem(
1456 coreconfigitem(
1454 b'web', b'address', default=b'',
1457 b'web', b'address', default=b'',
1455 )
1458 )
1456 coreconfigitem(
1459 coreconfigitem(
1457 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1460 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1458 )
1461 )
1459 coreconfigitem(
1462 coreconfigitem(
1460 b'web', b'allow_read', default=list,
1463 b'web', b'allow_read', default=list,
1461 )
1464 )
1462 coreconfigitem(
1465 coreconfigitem(
1463 b'web', b'baseurl', default=None,
1466 b'web', b'baseurl', default=None,
1464 )
1467 )
1465 coreconfigitem(
1468 coreconfigitem(
1466 b'web', b'cacerts', default=None,
1469 b'web', b'cacerts', default=None,
1467 )
1470 )
1468 coreconfigitem(
1471 coreconfigitem(
1469 b'web', b'certificate', default=None,
1472 b'web', b'certificate', default=None,
1470 )
1473 )
1471 coreconfigitem(
1474 coreconfigitem(
1472 b'web', b'collapse', default=False,
1475 b'web', b'collapse', default=False,
1473 )
1476 )
1474 coreconfigitem(
1477 coreconfigitem(
1475 b'web', b'csp', default=None,
1478 b'web', b'csp', default=None,
1476 )
1479 )
1477 coreconfigitem(
1480 coreconfigitem(
1478 b'web', b'deny_read', default=list,
1481 b'web', b'deny_read', default=list,
1479 )
1482 )
1480 coreconfigitem(
1483 coreconfigitem(
1481 b'web', b'descend', default=True,
1484 b'web', b'descend', default=True,
1482 )
1485 )
1483 coreconfigitem(
1486 coreconfigitem(
1484 b'web', b'description', default=b"",
1487 b'web', b'description', default=b"",
1485 )
1488 )
1486 coreconfigitem(
1489 coreconfigitem(
1487 b'web', b'encoding', default=lambda: encoding.encoding,
1490 b'web', b'encoding', default=lambda: encoding.encoding,
1488 )
1491 )
1489 coreconfigitem(
1492 coreconfigitem(
1490 b'web', b'errorlog', default=b'-',
1493 b'web', b'errorlog', default=b'-',
1491 )
1494 )
1492 coreconfigitem(
1495 coreconfigitem(
1493 b'web', b'ipv6', default=False,
1496 b'web', b'ipv6', default=False,
1494 )
1497 )
1495 coreconfigitem(
1498 coreconfigitem(
1496 b'web', b'maxchanges', default=10,
1499 b'web', b'maxchanges', default=10,
1497 )
1500 )
1498 coreconfigitem(
1501 coreconfigitem(
1499 b'web', b'maxfiles', default=10,
1502 b'web', b'maxfiles', default=10,
1500 )
1503 )
1501 coreconfigitem(
1504 coreconfigitem(
1502 b'web', b'maxshortchanges', default=60,
1505 b'web', b'maxshortchanges', default=60,
1503 )
1506 )
1504 coreconfigitem(
1507 coreconfigitem(
1505 b'web', b'motd', default=b'',
1508 b'web', b'motd', default=b'',
1506 )
1509 )
1507 coreconfigitem(
1510 coreconfigitem(
1508 b'web', b'name', default=dynamicdefault,
1511 b'web', b'name', default=dynamicdefault,
1509 )
1512 )
1510 coreconfigitem(
1513 coreconfigitem(
1511 b'web', b'port', default=8000,
1514 b'web', b'port', default=8000,
1512 )
1515 )
1513 coreconfigitem(
1516 coreconfigitem(
1514 b'web', b'prefix', default=b'',
1517 b'web', b'prefix', default=b'',
1515 )
1518 )
1516 coreconfigitem(
1519 coreconfigitem(
1517 b'web', b'push_ssl', default=True,
1520 b'web', b'push_ssl', default=True,
1518 )
1521 )
1519 coreconfigitem(
1522 coreconfigitem(
1520 b'web', b'refreshinterval', default=20,
1523 b'web', b'refreshinterval', default=20,
1521 )
1524 )
1522 coreconfigitem(
1525 coreconfigitem(
1523 b'web', b'server-header', default=None,
1526 b'web', b'server-header', default=None,
1524 )
1527 )
1525 coreconfigitem(
1528 coreconfigitem(
1526 b'web', b'static', default=None,
1529 b'web', b'static', default=None,
1527 )
1530 )
1528 coreconfigitem(
1531 coreconfigitem(
1529 b'web', b'staticurl', default=None,
1532 b'web', b'staticurl', default=None,
1530 )
1533 )
1531 coreconfigitem(
1534 coreconfigitem(
1532 b'web', b'stripes', default=1,
1535 b'web', b'stripes', default=1,
1533 )
1536 )
1534 coreconfigitem(
1537 coreconfigitem(
1535 b'web', b'style', default=b'paper',
1538 b'web', b'style', default=b'paper',
1536 )
1539 )
1537 coreconfigitem(
1540 coreconfigitem(
1538 b'web', b'templates', default=None,
1541 b'web', b'templates', default=None,
1539 )
1542 )
1540 coreconfigitem(
1543 coreconfigitem(
1541 b'web', b'view', default=b'served', experimental=True,
1544 b'web', b'view', default=b'served', experimental=True,
1542 )
1545 )
1543 coreconfigitem(
1546 coreconfigitem(
1544 b'worker', b'backgroundclose', default=dynamicdefault,
1547 b'worker', b'backgroundclose', default=dynamicdefault,
1545 )
1548 )
1546 # Windows defaults to a limit of 512 open files. A buffer of 128
1549 # Windows defaults to a limit of 512 open files. A buffer of 128
1547 # should give us enough headway.
1550 # should give us enough headway.
1548 coreconfigitem(
1551 coreconfigitem(
1549 b'worker', b'backgroundclosemaxqueue', default=384,
1552 b'worker', b'backgroundclosemaxqueue', default=384,
1550 )
1553 )
1551 coreconfigitem(
1554 coreconfigitem(
1552 b'worker', b'backgroundcloseminfilecount', default=2048,
1555 b'worker', b'backgroundcloseminfilecount', default=2048,
1553 )
1556 )
1554 coreconfigitem(
1557 coreconfigitem(
1555 b'worker', b'backgroundclosethreadcount', default=4,
1558 b'worker', b'backgroundclosethreadcount', default=4,
1556 )
1559 )
1557 coreconfigitem(
1560 coreconfigitem(
1558 b'worker', b'enabled', default=True,
1561 b'worker', b'enabled', default=True,
1559 )
1562 )
1560 coreconfigitem(
1563 coreconfigitem(
1561 b'worker', b'numcpus', default=None,
1564 b'worker', b'numcpus', default=None,
1562 )
1565 )
1563
1566
1564 # Rebase related configuration moved to core because other extension are doing
1567 # Rebase related configuration moved to core because other extension are doing
1565 # strange things. For example, shelve import the extensions to reuse some bit
1568 # strange things. For example, shelve import the extensions to reuse some bit
1566 # without formally loading it.
1569 # without formally loading it.
1567 coreconfigitem(
1570 coreconfigitem(
1568 b'commands', b'rebase.requiredest', default=False,
1571 b'commands', b'rebase.requiredest', default=False,
1569 )
1572 )
1570 coreconfigitem(
1573 coreconfigitem(
1571 b'experimental', b'rebaseskipobsolete', default=True,
1574 b'experimental', b'rebaseskipobsolete', default=True,
1572 )
1575 )
1573 coreconfigitem(
1576 coreconfigitem(
1574 b'rebase', b'singletransaction', default=False,
1577 b'rebase', b'singletransaction', default=False,
1575 )
1578 )
1576 coreconfigitem(
1579 coreconfigitem(
1577 b'rebase', b'experimental.inmemory', default=False,
1580 b'rebase', b'experimental.inmemory', default=False,
1578 )
1581 )
@@ -1,3112 +1,3140 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 weakref
11
12
12 from .i18n import _
13 from .i18n import _
13 from .node import (
14 from .node import (
14 hex,
15 hex,
15 nullid,
16 nullid,
16 nullrev,
17 nullrev,
17 )
18 )
18 from .thirdparty import attr
19 from .thirdparty import attr
19 from . import (
20 from . import (
20 bookmarks as bookmod,
21 bookmarks as bookmod,
21 bundle2,
22 bundle2,
22 changegroup,
23 changegroup,
23 discovery,
24 discovery,
24 error,
25 error,
25 exchangev2,
26 exchangev2,
26 lock as lockmod,
27 lock as lockmod,
27 logexchange,
28 logexchange,
28 narrowspec,
29 narrowspec,
29 obsolete,
30 obsolete,
30 obsutil,
31 obsutil,
31 phases,
32 phases,
32 pushkey,
33 pushkey,
33 pycompat,
34 pycompat,
34 scmutil,
35 scmutil,
35 sslutil,
36 sslutil,
36 streamclone,
37 streamclone,
37 url as urlmod,
38 url as urlmod,
38 util,
39 util,
39 wireprototypes,
40 wireprototypes,
40 )
41 )
41 from .interfaces import repository
42 from .interfaces import repository
42 from .utils import (
43 from .utils import (
43 hashutil,
44 hashutil,
44 stringutil,
45 stringutil,
45 )
46 )
46
47
47 urlerr = util.urlerr
48 urlerr = util.urlerr
48 urlreq = util.urlreq
49 urlreq = util.urlreq
49
50
50 _NARROWACL_SECTION = b'narrowacl'
51 _NARROWACL_SECTION = b'narrowacl'
51
52
52 # Maps bundle version human names to changegroup versions.
53 # Maps bundle version human names to changegroup versions.
53 _bundlespeccgversions = {
54 _bundlespeccgversions = {
54 b'v1': b'01',
55 b'v1': b'01',
55 b'v2': b'02',
56 b'v2': b'02',
56 b'packed1': b's1',
57 b'packed1': b's1',
57 b'bundle2': b'02', # legacy
58 b'bundle2': b'02', # legacy
58 }
59 }
59
60
60 # 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
61 _bundlespeccontentopts = {
62 _bundlespeccontentopts = {
62 b'v1': {
63 b'v1': {
63 b'changegroup': True,
64 b'changegroup': True,
64 b'cg.version': b'01',
65 b'cg.version': b'01',
65 b'obsolescence': False,
66 b'obsolescence': False,
66 b'phases': False,
67 b'phases': False,
67 b'tagsfnodescache': False,
68 b'tagsfnodescache': False,
68 b'revbranchcache': False,
69 b'revbranchcache': False,
69 },
70 },
70 b'v2': {
71 b'v2': {
71 b'changegroup': True,
72 b'changegroup': True,
72 b'cg.version': b'02',
73 b'cg.version': b'02',
73 b'obsolescence': False,
74 b'obsolescence': False,
74 b'phases': False,
75 b'phases': False,
75 b'tagsfnodescache': True,
76 b'tagsfnodescache': True,
76 b'revbranchcache': True,
77 b'revbranchcache': True,
77 },
78 },
78 b'packed1': {b'cg.version': b's1'},
79 b'packed1': {b'cg.version': b's1'},
79 }
80 }
80 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2']
81 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2']
81
82
82 _bundlespecvariants = {
83 _bundlespecvariants = {
83 b"streamv2": {
84 b"streamv2": {
84 b"changegroup": False,
85 b"changegroup": False,
85 b"streamv2": True,
86 b"streamv2": True,
86 b"tagsfnodescache": False,
87 b"tagsfnodescache": False,
87 b"revbranchcache": False,
88 b"revbranchcache": False,
88 }
89 }
89 }
90 }
90
91
91 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
92 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
92 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'}
93 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'}
93
94
94
95
95 @attr.s
96 @attr.s
96 class bundlespec(object):
97 class bundlespec(object):
97 compression = attr.ib()
98 compression = attr.ib()
98 wirecompression = attr.ib()
99 wirecompression = attr.ib()
99 version = attr.ib()
100 version = attr.ib()
100 wireversion = attr.ib()
101 wireversion = attr.ib()
101 params = attr.ib()
102 params = attr.ib()
102 contentopts = attr.ib()
103 contentopts = attr.ib()
103
104
104
105
105 def parsebundlespec(repo, spec, strict=True):
106 def parsebundlespec(repo, spec, strict=True):
106 """Parse a bundle string specification into parts.
107 """Parse a bundle string specification into parts.
107
108
108 Bundle specifications denote a well-defined bundle/exchange format.
109 Bundle specifications denote a well-defined bundle/exchange format.
109 The content of a given specification should not change over time in
110 The content of a given specification should not change over time in
110 order to ensure that bundles produced by a newer version of Mercurial are
111 order to ensure that bundles produced by a newer version of Mercurial are
111 readable from an older version.
112 readable from an older version.
112
113
113 The string currently has the form:
114 The string currently has the form:
114
115
115 <compression>-<type>[;<parameter0>[;<parameter1>]]
116 <compression>-<type>[;<parameter0>[;<parameter1>]]
116
117
117 Where <compression> is one of the supported compression formats
118 Where <compression> is one of the supported compression formats
118 and <type> is (currently) a version string. A ";" can follow the type and
119 and <type> is (currently) a version string. A ";" can follow the type and
119 all text afterwards is interpreted as URI encoded, ";" delimited key=value
120 all text afterwards is interpreted as URI encoded, ";" delimited key=value
120 pairs.
121 pairs.
121
122
122 If ``strict`` is True (the default) <compression> is required. Otherwise,
123 If ``strict`` is True (the default) <compression> is required. Otherwise,
123 it is optional.
124 it is optional.
124
125
125 Returns a bundlespec object of (compression, version, parameters).
126 Returns a bundlespec object of (compression, version, parameters).
126 Compression will be ``None`` if not in strict mode and a compression isn't
127 Compression will be ``None`` if not in strict mode and a compression isn't
127 defined.
128 defined.
128
129
129 An ``InvalidBundleSpecification`` is raised when the specification is
130 An ``InvalidBundleSpecification`` is raised when the specification is
130 not syntactically well formed.
131 not syntactically well formed.
131
132
132 An ``UnsupportedBundleSpecification`` is raised when the compression or
133 An ``UnsupportedBundleSpecification`` is raised when the compression or
133 bundle type/version is not recognized.
134 bundle type/version is not recognized.
134
135
135 Note: this function will likely eventually return a more complex data
136 Note: this function will likely eventually return a more complex data
136 structure, including bundle2 part information.
137 structure, including bundle2 part information.
137 """
138 """
138
139
139 def parseparams(s):
140 def parseparams(s):
140 if b';' not in s:
141 if b';' not in s:
141 return s, {}
142 return s, {}
142
143
143 params = {}
144 params = {}
144 version, paramstr = s.split(b';', 1)
145 version, paramstr = s.split(b';', 1)
145
146
146 for p in paramstr.split(b';'):
147 for p in paramstr.split(b';'):
147 if b'=' not in p:
148 if b'=' not in p:
148 raise error.InvalidBundleSpecification(
149 raise error.InvalidBundleSpecification(
149 _(
150 _(
150 b'invalid bundle specification: '
151 b'invalid bundle specification: '
151 b'missing "=" in parameter: %s'
152 b'missing "=" in parameter: %s'
152 )
153 )
153 % p
154 % p
154 )
155 )
155
156
156 key, value = p.split(b'=', 1)
157 key, value = p.split(b'=', 1)
157 key = urlreq.unquote(key)
158 key = urlreq.unquote(key)
158 value = urlreq.unquote(value)
159 value = urlreq.unquote(value)
159 params[key] = value
160 params[key] = value
160
161
161 return version, params
162 return version, params
162
163
163 if strict and b'-' not in spec:
164 if strict and b'-' not in spec:
164 raise error.InvalidBundleSpecification(
165 raise error.InvalidBundleSpecification(
165 _(
166 _(
166 b'invalid bundle specification; '
167 b'invalid bundle specification; '
167 b'must be prefixed with compression: %s'
168 b'must be prefixed with compression: %s'
168 )
169 )
169 % spec
170 % spec
170 )
171 )
171
172
172 if b'-' in spec:
173 if b'-' in spec:
173 compression, version = spec.split(b'-', 1)
174 compression, version = spec.split(b'-', 1)
174
175
175 if compression not in util.compengines.supportedbundlenames:
176 if compression not in util.compengines.supportedbundlenames:
176 raise error.UnsupportedBundleSpecification(
177 raise error.UnsupportedBundleSpecification(
177 _(b'%s compression is not supported') % compression
178 _(b'%s compression is not supported') % compression
178 )
179 )
179
180
180 version, params = parseparams(version)
181 version, params = parseparams(version)
181
182
182 if version not in _bundlespeccgversions:
183 if version not in _bundlespeccgversions:
183 raise error.UnsupportedBundleSpecification(
184 raise error.UnsupportedBundleSpecification(
184 _(b'%s is not a recognized bundle version') % version
185 _(b'%s is not a recognized bundle version') % version
185 )
186 )
186 else:
187 else:
187 # Value could be just the compression or just the version, in which
188 # Value could be just the compression or just the version, in which
188 # case some defaults are assumed (but only when not in strict mode).
189 # case some defaults are assumed (but only when not in strict mode).
189 assert not strict
190 assert not strict
190
191
191 spec, params = parseparams(spec)
192 spec, params = parseparams(spec)
192
193
193 if spec in util.compengines.supportedbundlenames:
194 if spec in util.compengines.supportedbundlenames:
194 compression = spec
195 compression = spec
195 version = b'v1'
196 version = b'v1'
196 # Generaldelta repos require v2.
197 # Generaldelta repos require v2.
197 if b'generaldelta' in repo.requirements:
198 if b'generaldelta' in repo.requirements:
198 version = b'v2'
199 version = b'v2'
199 # Modern compression engines require v2.
200 # Modern compression engines require v2.
200 if compression not in _bundlespecv1compengines:
201 if compression not in _bundlespecv1compengines:
201 version = b'v2'
202 version = b'v2'
202 elif spec in _bundlespeccgversions:
203 elif spec in _bundlespeccgversions:
203 if spec == b'packed1':
204 if spec == b'packed1':
204 compression = b'none'
205 compression = b'none'
205 else:
206 else:
206 compression = b'bzip2'
207 compression = b'bzip2'
207 version = spec
208 version = spec
208 else:
209 else:
209 raise error.UnsupportedBundleSpecification(
210 raise error.UnsupportedBundleSpecification(
210 _(b'%s is not a recognized bundle specification') % spec
211 _(b'%s is not a recognized bundle specification') % spec
211 )
212 )
212
213
213 # Bundle version 1 only supports a known set of compression engines.
214 # Bundle version 1 only supports a known set of compression engines.
214 if version == b'v1' and compression not in _bundlespecv1compengines:
215 if version == b'v1' and compression not in _bundlespecv1compengines:
215 raise error.UnsupportedBundleSpecification(
216 raise error.UnsupportedBundleSpecification(
216 _(b'compression engine %s is not supported on v1 bundles')
217 _(b'compression engine %s is not supported on v1 bundles')
217 % compression
218 % compression
218 )
219 )
219
220
220 # The specification for packed1 can optionally declare the data formats
221 # The specification for packed1 can optionally declare the data formats
221 # required to apply it. If we see this metadata, compare against what the
222 # required to apply it. If we see this metadata, compare against what the
222 # repo supports and error if the bundle isn't compatible.
223 # repo supports and error if the bundle isn't compatible.
223 if version == b'packed1' and b'requirements' in params:
224 if version == b'packed1' and b'requirements' in params:
224 requirements = set(params[b'requirements'].split(b','))
225 requirements = set(params[b'requirements'].split(b','))
225 missingreqs = requirements - repo.supportedformats
226 missingreqs = requirements - repo.supportedformats
226 if missingreqs:
227 if missingreqs:
227 raise error.UnsupportedBundleSpecification(
228 raise error.UnsupportedBundleSpecification(
228 _(b'missing support for repository features: %s')
229 _(b'missing support for repository features: %s')
229 % b', '.join(sorted(missingreqs))
230 % b', '.join(sorted(missingreqs))
230 )
231 )
231
232
232 # Compute contentopts based on the version
233 # Compute contentopts based on the version
233 contentopts = _bundlespeccontentopts.get(version, {}).copy()
234 contentopts = _bundlespeccontentopts.get(version, {}).copy()
234
235
235 # Process the variants
236 # Process the variants
236 if b"stream" in params and params[b"stream"] == b"v2":
237 if b"stream" in params and params[b"stream"] == b"v2":
237 variant = _bundlespecvariants[b"streamv2"]
238 variant = _bundlespecvariants[b"streamv2"]
238 contentopts.update(variant)
239 contentopts.update(variant)
239
240
240 engine = util.compengines.forbundlename(compression)
241 engine = util.compengines.forbundlename(compression)
241 compression, wirecompression = engine.bundletype()
242 compression, wirecompression = engine.bundletype()
242 wireversion = _bundlespeccgversions[version]
243 wireversion = _bundlespeccgversions[version]
243
244
244 return bundlespec(
245 return bundlespec(
245 compression, wirecompression, version, wireversion, params, contentopts
246 compression, wirecompression, version, wireversion, params, contentopts
246 )
247 )
247
248
248
249
249 def readbundle(ui, fh, fname, vfs=None):
250 def readbundle(ui, fh, fname, vfs=None):
250 header = changegroup.readexactly(fh, 4)
251 header = changegroup.readexactly(fh, 4)
251
252
252 alg = None
253 alg = None
253 if not fname:
254 if not fname:
254 fname = b"stream"
255 fname = b"stream"
255 if not header.startswith(b'HG') and header.startswith(b'\0'):
256 if not header.startswith(b'HG') and header.startswith(b'\0'):
256 fh = changegroup.headerlessfixup(fh, header)
257 fh = changegroup.headerlessfixup(fh, header)
257 header = b"HG10"
258 header = b"HG10"
258 alg = b'UN'
259 alg = b'UN'
259 elif vfs:
260 elif vfs:
260 fname = vfs.join(fname)
261 fname = vfs.join(fname)
261
262
262 magic, version = header[0:2], header[2:4]
263 magic, version = header[0:2], header[2:4]
263
264
264 if magic != b'HG':
265 if magic != b'HG':
265 raise error.Abort(_(b'%s: not a Mercurial bundle') % fname)
266 raise error.Abort(_(b'%s: not a Mercurial bundle') % fname)
266 if version == b'10':
267 if version == b'10':
267 if alg is None:
268 if alg is None:
268 alg = changegroup.readexactly(fh, 2)
269 alg = changegroup.readexactly(fh, 2)
269 return changegroup.cg1unpacker(fh, alg)
270 return changegroup.cg1unpacker(fh, alg)
270 elif version.startswith(b'2'):
271 elif version.startswith(b'2'):
271 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
272 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
272 elif version == b'S1':
273 elif version == b'S1':
273 return streamclone.streamcloneapplier(fh)
274 return streamclone.streamcloneapplier(fh)
274 else:
275 else:
275 raise error.Abort(
276 raise error.Abort(
276 _(b'%s: unknown bundle version %s') % (fname, version)
277 _(b'%s: unknown bundle version %s') % (fname, version)
277 )
278 )
278
279
279
280
280 def getbundlespec(ui, fh):
281 def getbundlespec(ui, fh):
281 """Infer the bundlespec from a bundle file handle.
282 """Infer the bundlespec from a bundle file handle.
282
283
283 The input file handle is seeked and the original seek position is not
284 The input file handle is seeked and the original seek position is not
284 restored.
285 restored.
285 """
286 """
286
287
287 def speccompression(alg):
288 def speccompression(alg):
288 try:
289 try:
289 return util.compengines.forbundletype(alg).bundletype()[0]
290 return util.compengines.forbundletype(alg).bundletype()[0]
290 except KeyError:
291 except KeyError:
291 return None
292 return None
292
293
293 b = readbundle(ui, fh, None)
294 b = readbundle(ui, fh, None)
294 if isinstance(b, changegroup.cg1unpacker):
295 if isinstance(b, changegroup.cg1unpacker):
295 alg = b._type
296 alg = b._type
296 if alg == b'_truncatedBZ':
297 if alg == b'_truncatedBZ':
297 alg = b'BZ'
298 alg = b'BZ'
298 comp = speccompression(alg)
299 comp = speccompression(alg)
299 if not comp:
300 if not comp:
300 raise error.Abort(_(b'unknown compression algorithm: %s') % alg)
301 raise error.Abort(_(b'unknown compression algorithm: %s') % alg)
301 return b'%s-v1' % comp
302 return b'%s-v1' % comp
302 elif isinstance(b, bundle2.unbundle20):
303 elif isinstance(b, bundle2.unbundle20):
303 if b'Compression' in b.params:
304 if b'Compression' in b.params:
304 comp = speccompression(b.params[b'Compression'])
305 comp = speccompression(b.params[b'Compression'])
305 if not comp:
306 if not comp:
306 raise error.Abort(
307 raise error.Abort(
307 _(b'unknown compression algorithm: %s') % comp
308 _(b'unknown compression algorithm: %s') % comp
308 )
309 )
309 else:
310 else:
310 comp = b'none'
311 comp = b'none'
311
312
312 version = None
313 version = None
313 for part in b.iterparts():
314 for part in b.iterparts():
314 if part.type == b'changegroup':
315 if part.type == b'changegroup':
315 version = part.params[b'version']
316 version = part.params[b'version']
316 if version in (b'01', b'02'):
317 if version in (b'01', b'02'):
317 version = b'v2'
318 version = b'v2'
318 else:
319 else:
319 raise error.Abort(
320 raise error.Abort(
320 _(
321 _(
321 b'changegroup version %s does not have '
322 b'changegroup version %s does not have '
322 b'a known bundlespec'
323 b'a known bundlespec'
323 )
324 )
324 % version,
325 % version,
325 hint=_(b'try upgrading your Mercurial client'),
326 hint=_(b'try upgrading your Mercurial client'),
326 )
327 )
327 elif part.type == b'stream2' and version is None:
328 elif part.type == b'stream2' and version is None:
328 # A stream2 part requires to be part of a v2 bundle
329 # A stream2 part requires to be part of a v2 bundle
329 requirements = urlreq.unquote(part.params[b'requirements'])
330 requirements = urlreq.unquote(part.params[b'requirements'])
330 splitted = requirements.split()
331 splitted = requirements.split()
331 params = bundle2._formatrequirementsparams(splitted)
332 params = bundle2._formatrequirementsparams(splitted)
332 return b'none-v2;stream=v2;%s' % params
333 return b'none-v2;stream=v2;%s' % params
333
334
334 if not version:
335 if not version:
335 raise error.Abort(
336 raise error.Abort(
336 _(b'could not identify changegroup version in bundle')
337 _(b'could not identify changegroup version in bundle')
337 )
338 )
338
339
339 return b'%s-%s' % (comp, version)
340 return b'%s-%s' % (comp, version)
340 elif isinstance(b, streamclone.streamcloneapplier):
341 elif isinstance(b, streamclone.streamcloneapplier):
341 requirements = streamclone.readbundle1header(fh)[2]
342 requirements = streamclone.readbundle1header(fh)[2]
342 formatted = bundle2._formatrequirementsparams(requirements)
343 formatted = bundle2._formatrequirementsparams(requirements)
343 return b'none-packed1;%s' % formatted
344 return b'none-packed1;%s' % formatted
344 else:
345 else:
345 raise error.Abort(_(b'unknown bundle type: %s') % b)
346 raise error.Abort(_(b'unknown bundle type: %s') % b)
346
347
347
348
348 def _computeoutgoing(repo, heads, common):
349 def _computeoutgoing(repo, heads, common):
349 """Computes which revs are outgoing given a set of common
350 """Computes which revs are outgoing given a set of common
350 and a set of heads.
351 and a set of heads.
351
352
352 This is a separate function so extensions can have access to
353 This is a separate function so extensions can have access to
353 the logic.
354 the logic.
354
355
355 Returns a discovery.outgoing object.
356 Returns a discovery.outgoing object.
356 """
357 """
357 cl = repo.changelog
358 cl = repo.changelog
358 if common:
359 if common:
359 hasnode = cl.hasnode
360 hasnode = cl.hasnode
360 common = [n for n in common if hasnode(n)]
361 common = [n for n in common if hasnode(n)]
361 else:
362 else:
362 common = [nullid]
363 common = [nullid]
363 if not heads:
364 if not heads:
364 heads = cl.heads()
365 heads = cl.heads()
365 return discovery.outgoing(repo, common, heads)
366 return discovery.outgoing(repo, common, heads)
366
367
367
368
368 def _checkpublish(pushop):
369 def _checkpublish(pushop):
369 repo = pushop.repo
370 repo = pushop.repo
370 ui = repo.ui
371 ui = repo.ui
371 behavior = ui.config(b'experimental', b'auto-publish')
372 behavior = ui.config(b'experimental', b'auto-publish')
372 if pushop.publish or behavior not in (b'warn', b'confirm', b'abort'):
373 if pushop.publish or behavior not in (b'warn', b'confirm', b'abort'):
373 return
374 return
374 remotephases = listkeys(pushop.remote, b'phases')
375 remotephases = listkeys(pushop.remote, b'phases')
375 if not remotephases.get(b'publishing', False):
376 if not remotephases.get(b'publishing', False):
376 return
377 return
377
378
378 if pushop.revs is None:
379 if pushop.revs is None:
379 published = repo.filtered(b'served').revs(b'not public()')
380 published = repo.filtered(b'served').revs(b'not public()')
380 else:
381 else:
381 published = repo.revs(b'::%ln - public()', pushop.revs)
382 published = repo.revs(b'::%ln - public()', pushop.revs)
382 if published:
383 if published:
383 if behavior == b'warn':
384 if behavior == b'warn':
384 ui.warn(
385 ui.warn(
385 _(b'%i changesets about to be published\n') % len(published)
386 _(b'%i changesets about to be published\n') % len(published)
386 )
387 )
387 elif behavior == b'confirm':
388 elif behavior == b'confirm':
388 if ui.promptchoice(
389 if ui.promptchoice(
389 _(b'push and publish %i changesets (yn)?$$ &Yes $$ &No')
390 _(b'push and publish %i changesets (yn)?$$ &Yes $$ &No')
390 % len(published)
391 % len(published)
391 ):
392 ):
392 raise error.Abort(_(b'user quit'))
393 raise error.Abort(_(b'user quit'))
393 elif behavior == b'abort':
394 elif behavior == b'abort':
394 msg = _(b'push would publish %i changesets') % len(published)
395 msg = _(b'push would publish %i changesets') % len(published)
395 hint = _(
396 hint = _(
396 b"use --publish or adjust 'experimental.auto-publish'"
397 b"use --publish or adjust 'experimental.auto-publish'"
397 b" config"
398 b" config"
398 )
399 )
399 raise error.Abort(msg, hint=hint)
400 raise error.Abort(msg, hint=hint)
400
401
401
402
402 def _forcebundle1(op):
403 def _forcebundle1(op):
403 """return true if a pull/push must use bundle1
404 """return true if a pull/push must use bundle1
404
405
405 This function is used to allow testing of the older bundle version"""
406 This function is used to allow testing of the older bundle version"""
406 ui = op.repo.ui
407 ui = op.repo.ui
407 # The goal is this config is to allow developer to choose the bundle
408 # The goal is this config is to allow developer to choose the bundle
408 # version used during exchanged. This is especially handy during test.
409 # version used during exchanged. This is especially handy during test.
409 # Value is a list of bundle version to be picked from, highest version
410 # Value is a list of bundle version to be picked from, highest version
410 # should be used.
411 # should be used.
411 #
412 #
412 # developer config: devel.legacy.exchange
413 # developer config: devel.legacy.exchange
413 exchange = ui.configlist(b'devel', b'legacy.exchange')
414 exchange = ui.configlist(b'devel', b'legacy.exchange')
414 forcebundle1 = b'bundle2' not in exchange and b'bundle1' in exchange
415 forcebundle1 = b'bundle2' not in exchange and b'bundle1' in exchange
415 return forcebundle1 or not op.remote.capable(b'bundle2')
416 return forcebundle1 or not op.remote.capable(b'bundle2')
416
417
417
418
418 class pushoperation(object):
419 class pushoperation(object):
419 """A object that represent a single push operation
420 """A object that represent a single push operation
420
421
421 Its purpose is to carry push related state and very common operations.
422 Its purpose is to carry push related state and very common operations.
422
423
423 A new pushoperation should be created at the beginning of each push and
424 A new pushoperation should be created at the beginning of each push and
424 discarded afterward.
425 discarded afterward.
425 """
426 """
426
427
427 def __init__(
428 def __init__(
428 self,
429 self,
429 repo,
430 repo,
430 remote,
431 remote,
431 force=False,
432 force=False,
432 revs=None,
433 revs=None,
433 newbranch=False,
434 newbranch=False,
434 bookmarks=(),
435 bookmarks=(),
435 publish=False,
436 publish=False,
436 pushvars=None,
437 pushvars=None,
437 ):
438 ):
438 # repo we push from
439 # repo we push from
439 self.repo = repo
440 self.repo = repo
440 self.ui = repo.ui
441 self.ui = repo.ui
441 # repo we push to
442 # repo we push to
442 self.remote = remote
443 self.remote = remote
443 # force option provided
444 # force option provided
444 self.force = force
445 self.force = force
445 # revs to be pushed (None is "all")
446 # revs to be pushed (None is "all")
446 self.revs = revs
447 self.revs = revs
447 # bookmark explicitly pushed
448 # bookmark explicitly pushed
448 self.bookmarks = bookmarks
449 self.bookmarks = bookmarks
449 # allow push of new branch
450 # allow push of new branch
450 self.newbranch = newbranch
451 self.newbranch = newbranch
451 # step already performed
452 # step already performed
452 # (used to check what steps have been already performed through bundle2)
453 # (used to check what steps have been already performed through bundle2)
453 self.stepsdone = set()
454 self.stepsdone = set()
454 # Integer version of the changegroup push result
455 # Integer version of the changegroup push result
455 # - None means nothing to push
456 # - None means nothing to push
456 # - 0 means HTTP error
457 # - 0 means HTTP error
457 # - 1 means we pushed and remote head count is unchanged *or*
458 # - 1 means we pushed and remote head count is unchanged *or*
458 # we have outgoing changesets but refused to push
459 # we have outgoing changesets but refused to push
459 # - other values as described by addchangegroup()
460 # - other values as described by addchangegroup()
460 self.cgresult = None
461 self.cgresult = None
461 # Boolean value for the bookmark push
462 # Boolean value for the bookmark push
462 self.bkresult = None
463 self.bkresult = None
463 # discover.outgoing object (contains common and outgoing data)
464 # discover.outgoing object (contains common and outgoing data)
464 self.outgoing = None
465 self.outgoing = None
465 # all remote topological heads before the push
466 # all remote topological heads before the push
466 self.remoteheads = None
467 self.remoteheads = None
467 # Details of the remote branch pre and post push
468 # Details of the remote branch pre and post push
468 #
469 #
469 # mapping: {'branch': ([remoteheads],
470 # mapping: {'branch': ([remoteheads],
470 # [newheads],
471 # [newheads],
471 # [unsyncedheads],
472 # [unsyncedheads],
472 # [discardedheads])}
473 # [discardedheads])}
473 # - branch: the branch name
474 # - branch: the branch name
474 # - remoteheads: the list of remote heads known locally
475 # - remoteheads: the list of remote heads known locally
475 # None if the branch is new
476 # None if the branch is new
476 # - newheads: the new remote heads (known locally) with outgoing pushed
477 # - newheads: the new remote heads (known locally) with outgoing pushed
477 # - unsyncedheads: the list of remote heads unknown locally.
478 # - unsyncedheads: the list of remote heads unknown locally.
478 # - discardedheads: the list of remote heads made obsolete by the push
479 # - discardedheads: the list of remote heads made obsolete by the push
479 self.pushbranchmap = None
480 self.pushbranchmap = None
480 # testable as a boolean indicating if any nodes are missing locally.
481 # testable as a boolean indicating if any nodes are missing locally.
481 self.incoming = None
482 self.incoming = None
482 # summary of the remote phase situation
483 # summary of the remote phase situation
483 self.remotephases = None
484 self.remotephases = None
484 # phases changes that must be pushed along side the changesets
485 # phases changes that must be pushed along side the changesets
485 self.outdatedphases = None
486 self.outdatedphases = None
486 # phases changes that must be pushed if changeset push fails
487 # phases changes that must be pushed if changeset push fails
487 self.fallbackoutdatedphases = None
488 self.fallbackoutdatedphases = None
488 # outgoing obsmarkers
489 # outgoing obsmarkers
489 self.outobsmarkers = set()
490 self.outobsmarkers = set()
490 # outgoing bookmarks, list of (bm, oldnode | '', newnode | '')
491 # outgoing bookmarks, list of (bm, oldnode | '', newnode | '')
491 self.outbookmarks = []
492 self.outbookmarks = []
492 # transaction manager
493 # transaction manager
493 self.trmanager = None
494 self.trmanager = None
494 # map { pushkey partid -> callback handling failure}
495 # map { pushkey partid -> callback handling failure}
495 # used to handle exception from mandatory pushkey part failure
496 # used to handle exception from mandatory pushkey part failure
496 self.pkfailcb = {}
497 self.pkfailcb = {}
497 # an iterable of pushvars or None
498 # an iterable of pushvars or None
498 self.pushvars = pushvars
499 self.pushvars = pushvars
499 # publish pushed changesets
500 # publish pushed changesets
500 self.publish = publish
501 self.publish = publish
501
502
502 @util.propertycache
503 @util.propertycache
503 def futureheads(self):
504 def futureheads(self):
504 """future remote heads if the changeset push succeeds"""
505 """future remote heads if the changeset push succeeds"""
505 return self.outgoing.missingheads
506 return self.outgoing.missingheads
506
507
507 @util.propertycache
508 @util.propertycache
508 def fallbackheads(self):
509 def fallbackheads(self):
509 """future remote heads if the changeset push fails"""
510 """future remote heads if the changeset push fails"""
510 if self.revs is None:
511 if self.revs is None:
511 # not target to push, all common are relevant
512 # not target to push, all common are relevant
512 return self.outgoing.commonheads
513 return self.outgoing.commonheads
513 unfi = self.repo.unfiltered()
514 unfi = self.repo.unfiltered()
514 # I want cheads = heads(::missingheads and ::commonheads)
515 # I want cheads = heads(::missingheads and ::commonheads)
515 # (missingheads is revs with secret changeset filtered out)
516 # (missingheads is revs with secret changeset filtered out)
516 #
517 #
517 # This can be expressed as:
518 # This can be expressed as:
518 # cheads = ( (missingheads and ::commonheads)
519 # cheads = ( (missingheads and ::commonheads)
519 # + (commonheads and ::missingheads))"
520 # + (commonheads and ::missingheads))"
520 # )
521 # )
521 #
522 #
522 # while trying to push we already computed the following:
523 # while trying to push we already computed the following:
523 # common = (::commonheads)
524 # common = (::commonheads)
524 # missing = ((commonheads::missingheads) - commonheads)
525 # missing = ((commonheads::missingheads) - commonheads)
525 #
526 #
526 # We can pick:
527 # We can pick:
527 # * missingheads part of common (::commonheads)
528 # * missingheads part of common (::commonheads)
528 common = self.outgoing.common
529 common = self.outgoing.common
529 rev = self.repo.changelog.index.rev
530 rev = self.repo.changelog.index.rev
530 cheads = [node for node in self.revs if rev(node) in common]
531 cheads = [node for node in self.revs if rev(node) in common]
531 # and
532 # and
532 # * commonheads parents on missing
533 # * commonheads parents on missing
533 revset = unfi.set(
534 revset = unfi.set(
534 b'%ln and parents(roots(%ln))',
535 b'%ln and parents(roots(%ln))',
535 self.outgoing.commonheads,
536 self.outgoing.commonheads,
536 self.outgoing.missing,
537 self.outgoing.missing,
537 )
538 )
538 cheads.extend(c.node() for c in revset)
539 cheads.extend(c.node() for c in revset)
539 return cheads
540 return cheads
540
541
541 @property
542 @property
542 def commonheads(self):
543 def commonheads(self):
543 """set of all common heads after changeset bundle push"""
544 """set of all common heads after changeset bundle push"""
544 if self.cgresult:
545 if self.cgresult:
545 return self.futureheads
546 return self.futureheads
546 else:
547 else:
547 return self.fallbackheads
548 return self.fallbackheads
548
549
549
550
550 # mapping of message used when pushing bookmark
551 # mapping of message used when pushing bookmark
551 bookmsgmap = {
552 bookmsgmap = {
552 b'update': (
553 b'update': (
553 _(b"updating bookmark %s\n"),
554 _(b"updating bookmark %s\n"),
554 _(b'updating bookmark %s failed!\n'),
555 _(b'updating bookmark %s failed!\n'),
555 ),
556 ),
556 b'export': (
557 b'export': (
557 _(b"exporting bookmark %s\n"),
558 _(b"exporting bookmark %s\n"),
558 _(b'exporting bookmark %s failed!\n'),
559 _(b'exporting bookmark %s failed!\n'),
559 ),
560 ),
560 b'delete': (
561 b'delete': (
561 _(b"deleting remote bookmark %s\n"),
562 _(b"deleting remote bookmark %s\n"),
562 _(b'deleting remote bookmark %s failed!\n'),
563 _(b'deleting remote bookmark %s failed!\n'),
563 ),
564 ),
564 }
565 }
565
566
566
567
567 def push(
568 def push(
568 repo,
569 repo,
569 remote,
570 remote,
570 force=False,
571 force=False,
571 revs=None,
572 revs=None,
572 newbranch=False,
573 newbranch=False,
573 bookmarks=(),
574 bookmarks=(),
574 publish=False,
575 publish=False,
575 opargs=None,
576 opargs=None,
576 ):
577 ):
577 '''Push outgoing changesets (limited by revs) from a local
578 '''Push outgoing changesets (limited by revs) from a local
578 repository to remote. Return an integer:
579 repository to remote. Return an integer:
579 - None means nothing to push
580 - None means nothing to push
580 - 0 means HTTP error
581 - 0 means HTTP error
581 - 1 means we pushed and remote head count is unchanged *or*
582 - 1 means we pushed and remote head count is unchanged *or*
582 we have outgoing changesets but refused to push
583 we have outgoing changesets but refused to push
583 - other values as described by addchangegroup()
584 - other values as described by addchangegroup()
584 '''
585 '''
585 if opargs is None:
586 if opargs is None:
586 opargs = {}
587 opargs = {}
587 pushop = pushoperation(
588 pushop = pushoperation(
588 repo,
589 repo,
589 remote,
590 remote,
590 force,
591 force,
591 revs,
592 revs,
592 newbranch,
593 newbranch,
593 bookmarks,
594 bookmarks,
594 publish,
595 publish,
595 **pycompat.strkwargs(opargs)
596 **pycompat.strkwargs(opargs)
596 )
597 )
597 if pushop.remote.local():
598 if pushop.remote.local():
598 missing = (
599 missing = (
599 set(pushop.repo.requirements) - pushop.remote.local().supported
600 set(pushop.repo.requirements) - pushop.remote.local().supported
600 )
601 )
601 if missing:
602 if missing:
602 msg = _(
603 msg = _(
603 b"required features are not"
604 b"required features are not"
604 b" supported in the destination:"
605 b" supported in the destination:"
605 b" %s"
606 b" %s"
606 ) % (b', '.join(sorted(missing)))
607 ) % (b', '.join(sorted(missing)))
607 raise error.Abort(msg)
608 raise error.Abort(msg)
608
609
609 if not pushop.remote.canpush():
610 if not pushop.remote.canpush():
610 raise error.Abort(_(b"destination does not support push"))
611 raise error.Abort(_(b"destination does not support push"))
611
612
612 if not pushop.remote.capable(b'unbundle'):
613 if not pushop.remote.capable(b'unbundle'):
613 raise error.Abort(
614 raise error.Abort(
614 _(
615 _(
615 b'cannot push: destination does not support the '
616 b'cannot push: destination does not support the '
616 b'unbundle wire protocol command'
617 b'unbundle wire protocol command'
617 )
618 )
618 )
619 )
619
620
620 # get lock as we might write phase data
621 # get lock as we might write phase data
621 wlock = lock = None
622 wlock = lock = None
622 try:
623 try:
623 # bundle2 push may receive a reply bundle touching bookmarks
624 # bundle2 push may receive a reply bundle touching bookmarks
624 # requiring the wlock. Take it now to ensure proper ordering.
625 # requiring the wlock. Take it now to ensure proper ordering.
625 maypushback = pushop.ui.configbool(b'experimental', b'bundle2.pushback')
626 maypushback = pushop.ui.configbool(b'experimental', b'bundle2.pushback')
626 if (
627 if (
627 (not _forcebundle1(pushop))
628 (not _forcebundle1(pushop))
628 and maypushback
629 and maypushback
629 and not bookmod.bookmarksinstore(repo)
630 and not bookmod.bookmarksinstore(repo)
630 ):
631 ):
631 wlock = pushop.repo.wlock()
632 wlock = pushop.repo.wlock()
632 lock = pushop.repo.lock()
633 lock = pushop.repo.lock()
633 pushop.trmanager = transactionmanager(
634 pushop.trmanager = transactionmanager(
634 pushop.repo, b'push-response', pushop.remote.url()
635 pushop.repo, b'push-response', pushop.remote.url()
635 )
636 )
636 except error.LockUnavailable as err:
637 except error.LockUnavailable as err:
637 # source repo cannot be locked.
638 # source repo cannot be locked.
638 # We do not abort the push, but just disable the local phase
639 # We do not abort the push, but just disable the local phase
639 # synchronisation.
640 # synchronisation.
640 msg = b'cannot lock source repository: %s\n' % stringutil.forcebytestr(
641 msg = b'cannot lock source repository: %s\n' % stringutil.forcebytestr(
641 err
642 err
642 )
643 )
643 pushop.ui.debug(msg)
644 pushop.ui.debug(msg)
644
645
645 with wlock or util.nullcontextmanager():
646 with wlock or util.nullcontextmanager():
646 with lock or util.nullcontextmanager():
647 with lock or util.nullcontextmanager():
647 with pushop.trmanager or util.nullcontextmanager():
648 with pushop.trmanager or util.nullcontextmanager():
648 pushop.repo.checkpush(pushop)
649 pushop.repo.checkpush(pushop)
649 _checkpublish(pushop)
650 _checkpublish(pushop)
650 _pushdiscovery(pushop)
651 _pushdiscovery(pushop)
651 if not pushop.force:
652 if not pushop.force:
652 _checksubrepostate(pushop)
653 _checksubrepostate(pushop)
653 if not _forcebundle1(pushop):
654 if not _forcebundle1(pushop):
654 _pushbundle2(pushop)
655 _pushbundle2(pushop)
655 _pushchangeset(pushop)
656 _pushchangeset(pushop)
656 _pushsyncphase(pushop)
657 _pushsyncphase(pushop)
657 _pushobsolete(pushop)
658 _pushobsolete(pushop)
658 _pushbookmark(pushop)
659 _pushbookmark(pushop)
659
660
660 if repo.ui.configbool(b'experimental', b'remotenames'):
661 if repo.ui.configbool(b'experimental', b'remotenames'):
661 logexchange.pullremotenames(repo, remote)
662 logexchange.pullremotenames(repo, remote)
662
663
663 return pushop
664 return pushop
664
665
665
666
666 # list of steps to perform discovery before push
667 # list of steps to perform discovery before push
667 pushdiscoveryorder = []
668 pushdiscoveryorder = []
668
669
669 # Mapping between step name and function
670 # Mapping between step name and function
670 #
671 #
671 # This exists to help extensions wrap steps if necessary
672 # This exists to help extensions wrap steps if necessary
672 pushdiscoverymapping = {}
673 pushdiscoverymapping = {}
673
674
674
675
675 def pushdiscovery(stepname):
676 def pushdiscovery(stepname):
676 """decorator for function performing discovery before push
677 """decorator for function performing discovery before push
677
678
678 The function is added to the step -> function mapping and appended to the
679 The function is added to the step -> function mapping and appended to the
679 list of steps. Beware that decorated function will be added in order (this
680 list of steps. Beware that decorated function will be added in order (this
680 may matter).
681 may matter).
681
682
682 You can only use this decorator for a new step, if you want to wrap a step
683 You can only use this decorator for a new step, if you want to wrap a step
683 from an extension, change the pushdiscovery dictionary directly."""
684 from an extension, change the pushdiscovery dictionary directly."""
684
685
685 def dec(func):
686 def dec(func):
686 assert stepname not in pushdiscoverymapping
687 assert stepname not in pushdiscoverymapping
687 pushdiscoverymapping[stepname] = func
688 pushdiscoverymapping[stepname] = func
688 pushdiscoveryorder.append(stepname)
689 pushdiscoveryorder.append(stepname)
689 return func
690 return func
690
691
691 return dec
692 return dec
692
693
693
694
694 def _pushdiscovery(pushop):
695 def _pushdiscovery(pushop):
695 """Run all discovery steps"""
696 """Run all discovery steps"""
696 for stepname in pushdiscoveryorder:
697 for stepname in pushdiscoveryorder:
697 step = pushdiscoverymapping[stepname]
698 step = pushdiscoverymapping[stepname]
698 step(pushop)
699 step(pushop)
699
700
700
701
701 def _checksubrepostate(pushop):
702 def _checksubrepostate(pushop):
702 """Ensure all outgoing referenced subrepo revisions are present locally"""
703 """Ensure all outgoing referenced subrepo revisions are present locally"""
703 for n in pushop.outgoing.missing:
704 for n in pushop.outgoing.missing:
704 ctx = pushop.repo[n]
705 ctx = pushop.repo[n]
705
706
706 if b'.hgsub' in ctx.manifest() and b'.hgsubstate' in ctx.files():
707 if b'.hgsub' in ctx.manifest() and b'.hgsubstate' in ctx.files():
707 for subpath in sorted(ctx.substate):
708 for subpath in sorted(ctx.substate):
708 sub = ctx.sub(subpath)
709 sub = ctx.sub(subpath)
709 sub.verify(onpush=True)
710 sub.verify(onpush=True)
710
711
711
712
712 @pushdiscovery(b'changeset')
713 @pushdiscovery(b'changeset')
713 def _pushdiscoverychangeset(pushop):
714 def _pushdiscoverychangeset(pushop):
714 """discover the changeset that need to be pushed"""
715 """discover the changeset that need to be pushed"""
715 fci = discovery.findcommonincoming
716 fci = discovery.findcommonincoming
716 if pushop.revs:
717 if pushop.revs:
717 commoninc = fci(
718 commoninc = fci(
718 pushop.repo,
719 pushop.repo,
719 pushop.remote,
720 pushop.remote,
720 force=pushop.force,
721 force=pushop.force,
721 ancestorsof=pushop.revs,
722 ancestorsof=pushop.revs,
722 )
723 )
723 else:
724 else:
724 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
725 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
725 common, inc, remoteheads = commoninc
726 common, inc, remoteheads = commoninc
726 fco = discovery.findcommonoutgoing
727 fco = discovery.findcommonoutgoing
727 outgoing = fco(
728 outgoing = fco(
728 pushop.repo,
729 pushop.repo,
729 pushop.remote,
730 pushop.remote,
730 onlyheads=pushop.revs,
731 onlyheads=pushop.revs,
731 commoninc=commoninc,
732 commoninc=commoninc,
732 force=pushop.force,
733 force=pushop.force,
733 )
734 )
734 pushop.outgoing = outgoing
735 pushop.outgoing = outgoing
735 pushop.remoteheads = remoteheads
736 pushop.remoteheads = remoteheads
736 pushop.incoming = inc
737 pushop.incoming = inc
737
738
738
739
739 @pushdiscovery(b'phase')
740 @pushdiscovery(b'phase')
740 def _pushdiscoveryphase(pushop):
741 def _pushdiscoveryphase(pushop):
741 """discover the phase that needs to be pushed
742 """discover the phase that needs to be pushed
742
743
743 (computed for both success and failure case for changesets push)"""
744 (computed for both success and failure case for changesets push)"""
744 outgoing = pushop.outgoing
745 outgoing = pushop.outgoing
745 unfi = pushop.repo.unfiltered()
746 unfi = pushop.repo.unfiltered()
746 remotephases = listkeys(pushop.remote, b'phases')
747 remotephases = listkeys(pushop.remote, b'phases')
747
748
748 if (
749 if (
749 pushop.ui.configbool(b'ui', b'_usedassubrepo')
750 pushop.ui.configbool(b'ui', b'_usedassubrepo')
750 and remotephases # server supports phases
751 and remotephases # server supports phases
751 and not pushop.outgoing.missing # no changesets to be pushed
752 and not pushop.outgoing.missing # no changesets to be pushed
752 and remotephases.get(b'publishing', False)
753 and remotephases.get(b'publishing', False)
753 ):
754 ):
754 # When:
755 # When:
755 # - this is a subrepo push
756 # - this is a subrepo push
756 # - and remote support phase
757 # - and remote support phase
757 # - and no changeset are to be pushed
758 # - and no changeset are to be pushed
758 # - and remote is publishing
759 # - and remote is publishing
759 # We may be in issue 3781 case!
760 # We may be in issue 3781 case!
760 # We drop the possible phase synchronisation done by
761 # We drop the possible phase synchronisation done by
761 # courtesy to publish changesets possibly locally draft
762 # courtesy to publish changesets possibly locally draft
762 # on the remote.
763 # on the remote.
763 pushop.outdatedphases = []
764 pushop.outdatedphases = []
764 pushop.fallbackoutdatedphases = []
765 pushop.fallbackoutdatedphases = []
765 return
766 return
766
767
767 pushop.remotephases = phases.remotephasessummary(
768 pushop.remotephases = phases.remotephasessummary(
768 pushop.repo, pushop.fallbackheads, remotephases
769 pushop.repo, pushop.fallbackheads, remotephases
769 )
770 )
770 droots = pushop.remotephases.draftroots
771 droots = pushop.remotephases.draftroots
771
772
772 extracond = b''
773 extracond = b''
773 if not pushop.remotephases.publishing:
774 if not pushop.remotephases.publishing:
774 extracond = b' and public()'
775 extracond = b' and public()'
775 revset = b'heads((%%ln::%%ln) %s)' % extracond
776 revset = b'heads((%%ln::%%ln) %s)' % extracond
776 # Get the list of all revs draft on remote by public here.
777 # Get the list of all revs draft on remote by public here.
777 # XXX Beware that revset break if droots is not strictly
778 # XXX Beware that revset break if droots is not strictly
778 # XXX root we may want to ensure it is but it is costly
779 # XXX root we may want to ensure it is but it is costly
779 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
780 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
780 if not pushop.remotephases.publishing and pushop.publish:
781 if not pushop.remotephases.publishing and pushop.publish:
781 future = list(
782 future = list(
782 unfi.set(
783 unfi.set(
783 b'%ln and (not public() or %ln::)', pushop.futureheads, droots
784 b'%ln and (not public() or %ln::)', pushop.futureheads, droots
784 )
785 )
785 )
786 )
786 elif not outgoing.missing:
787 elif not outgoing.missing:
787 future = fallback
788 future = fallback
788 else:
789 else:
789 # adds changeset we are going to push as draft
790 # adds changeset we are going to push as draft
790 #
791 #
791 # should not be necessary for publishing server, but because of an
792 # should not be necessary for publishing server, but because of an
792 # issue fixed in xxxxx we have to do it anyway.
793 # issue fixed in xxxxx we have to do it anyway.
793 fdroots = list(
794 fdroots = list(
794 unfi.set(b'roots(%ln + %ln::)', outgoing.missing, droots)
795 unfi.set(b'roots(%ln + %ln::)', outgoing.missing, droots)
795 )
796 )
796 fdroots = [f.node() for f in fdroots]
797 fdroots = [f.node() for f in fdroots]
797 future = list(unfi.set(revset, fdroots, pushop.futureheads))
798 future = list(unfi.set(revset, fdroots, pushop.futureheads))
798 pushop.outdatedphases = future
799 pushop.outdatedphases = future
799 pushop.fallbackoutdatedphases = fallback
800 pushop.fallbackoutdatedphases = fallback
800
801
801
802
802 @pushdiscovery(b'obsmarker')
803 @pushdiscovery(b'obsmarker')
803 def _pushdiscoveryobsmarkers(pushop):
804 def _pushdiscoveryobsmarkers(pushop):
804 if not obsolete.isenabled(pushop.repo, obsolete.exchangeopt):
805 if not obsolete.isenabled(pushop.repo, obsolete.exchangeopt):
805 return
806 return
806
807
807 if not pushop.repo.obsstore:
808 if not pushop.repo.obsstore:
808 return
809 return
809
810
810 if b'obsolete' not in listkeys(pushop.remote, b'namespaces'):
811 if b'obsolete' not in listkeys(pushop.remote, b'namespaces'):
811 return
812 return
812
813
813 repo = pushop.repo
814 repo = pushop.repo
814 # very naive computation, that can be quite expensive on big repo.
815 # very naive computation, that can be quite expensive on big repo.
815 # However: evolution is currently slow on them anyway.
816 # However: evolution is currently slow on them anyway.
816 nodes = (c.node() for c in repo.set(b'::%ln', pushop.futureheads))
817 nodes = (c.node() for c in repo.set(b'::%ln', pushop.futureheads))
817 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
818 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
818
819
819
820
820 @pushdiscovery(b'bookmarks')
821 @pushdiscovery(b'bookmarks')
821 def _pushdiscoverybookmarks(pushop):
822 def _pushdiscoverybookmarks(pushop):
822 ui = pushop.ui
823 ui = pushop.ui
823 repo = pushop.repo.unfiltered()
824 repo = pushop.repo.unfiltered()
824 remote = pushop.remote
825 remote = pushop.remote
825 ui.debug(b"checking for updated bookmarks\n")
826 ui.debug(b"checking for updated bookmarks\n")
826 ancestors = ()
827 ancestors = ()
827 if pushop.revs:
828 if pushop.revs:
828 revnums = pycompat.maplist(repo.changelog.rev, pushop.revs)
829 revnums = pycompat.maplist(repo.changelog.rev, pushop.revs)
829 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
830 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
830
831
831 remotebookmark = bookmod.unhexlifybookmarks(listkeys(remote, b'bookmarks'))
832 remotebookmark = bookmod.unhexlifybookmarks(listkeys(remote, b'bookmarks'))
832
833
833 explicit = {
834 explicit = {
834 repo._bookmarks.expandname(bookmark) for bookmark in pushop.bookmarks
835 repo._bookmarks.expandname(bookmark) for bookmark in pushop.bookmarks
835 }
836 }
836
837
837 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
838 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
838 return _processcompared(pushop, ancestors, explicit, remotebookmark, comp)
839 return _processcompared(pushop, ancestors, explicit, remotebookmark, comp)
839
840
840
841
841 def _processcompared(pushop, pushed, explicit, remotebms, comp):
842 def _processcompared(pushop, pushed, explicit, remotebms, comp):
842 """take decision on bookmarks to push to the remote repo
843 """take decision on bookmarks to push to the remote repo
843
844
844 Exists to help extensions alter this behavior.
845 Exists to help extensions alter this behavior.
845 """
846 """
846 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
847 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
847
848
848 repo = pushop.repo
849 repo = pushop.repo
849
850
850 for b, scid, dcid in advsrc:
851 for b, scid, dcid in advsrc:
851 if b in explicit:
852 if b in explicit:
852 explicit.remove(b)
853 explicit.remove(b)
853 if not pushed or repo[scid].rev() in pushed:
854 if not pushed or repo[scid].rev() in pushed:
854 pushop.outbookmarks.append((b, dcid, scid))
855 pushop.outbookmarks.append((b, dcid, scid))
855 # search added bookmark
856 # search added bookmark
856 for b, scid, dcid in addsrc:
857 for b, scid, dcid in addsrc:
857 if b in explicit:
858 if b in explicit:
858 explicit.remove(b)
859 explicit.remove(b)
859 if bookmod.isdivergent(b):
860 if bookmod.isdivergent(b):
860 pushop.ui.warn(_(b'cannot push divergent bookmark %s!\n') % b)
861 pushop.ui.warn(_(b'cannot push divergent bookmark %s!\n') % b)
861 pushop.bkresult = 2
862 pushop.bkresult = 2
862 else:
863 else:
863 pushop.outbookmarks.append((b, b'', scid))
864 pushop.outbookmarks.append((b, b'', scid))
864 # search for overwritten bookmark
865 # search for overwritten bookmark
865 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
866 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
866 if b in explicit:
867 if b in explicit:
867 explicit.remove(b)
868 explicit.remove(b)
868 pushop.outbookmarks.append((b, dcid, scid))
869 pushop.outbookmarks.append((b, dcid, scid))
869 # search for bookmark to delete
870 # search for bookmark to delete
870 for b, scid, dcid in adddst:
871 for b, scid, dcid in adddst:
871 if b in explicit:
872 if b in explicit:
872 explicit.remove(b)
873 explicit.remove(b)
873 # treat as "deleted locally"
874 # treat as "deleted locally"
874 pushop.outbookmarks.append((b, dcid, b''))
875 pushop.outbookmarks.append((b, dcid, b''))
875 # identical bookmarks shouldn't get reported
876 # identical bookmarks shouldn't get reported
876 for b, scid, dcid in same:
877 for b, scid, dcid in same:
877 if b in explicit:
878 if b in explicit:
878 explicit.remove(b)
879 explicit.remove(b)
879
880
880 if explicit:
881 if explicit:
881 explicit = sorted(explicit)
882 explicit = sorted(explicit)
882 # we should probably list all of them
883 # we should probably list all of them
883 pushop.ui.warn(
884 pushop.ui.warn(
884 _(
885 _(
885 b'bookmark %s does not exist on the local '
886 b'bookmark %s does not exist on the local '
886 b'or remote repository!\n'
887 b'or remote repository!\n'
887 )
888 )
888 % explicit[0]
889 % explicit[0]
889 )
890 )
890 pushop.bkresult = 2
891 pushop.bkresult = 2
891
892
892 pushop.outbookmarks.sort()
893 pushop.outbookmarks.sort()
893
894
894
895
895 def _pushcheckoutgoing(pushop):
896 def _pushcheckoutgoing(pushop):
896 outgoing = pushop.outgoing
897 outgoing = pushop.outgoing
897 unfi = pushop.repo.unfiltered()
898 unfi = pushop.repo.unfiltered()
898 if not outgoing.missing:
899 if not outgoing.missing:
899 # nothing to push
900 # nothing to push
900 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
901 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
901 return False
902 return False
902 # something to push
903 # something to push
903 if not pushop.force:
904 if not pushop.force:
904 # if repo.obsstore == False --> no obsolete
905 # if repo.obsstore == False --> no obsolete
905 # then, save the iteration
906 # then, save the iteration
906 if unfi.obsstore:
907 if unfi.obsstore:
907 # this message are here for 80 char limit reason
908 # this message are here for 80 char limit reason
908 mso = _(b"push includes obsolete changeset: %s!")
909 mso = _(b"push includes obsolete changeset: %s!")
909 mspd = _(b"push includes phase-divergent changeset: %s!")
910 mspd = _(b"push includes phase-divergent changeset: %s!")
910 mscd = _(b"push includes content-divergent changeset: %s!")
911 mscd = _(b"push includes content-divergent changeset: %s!")
911 mst = {
912 mst = {
912 b"orphan": _(b"push includes orphan changeset: %s!"),
913 b"orphan": _(b"push includes orphan changeset: %s!"),
913 b"phase-divergent": mspd,
914 b"phase-divergent": mspd,
914 b"content-divergent": mscd,
915 b"content-divergent": mscd,
915 }
916 }
916 # If we are to push if there is at least one
917 # If we are to push if there is at least one
917 # obsolete or unstable changeset in missing, at
918 # obsolete or unstable changeset in missing, at
918 # least one of the missinghead will be obsolete or
919 # least one of the missinghead will be obsolete or
919 # unstable. So checking heads only is ok
920 # unstable. So checking heads only is ok
920 for node in outgoing.missingheads:
921 for node in outgoing.missingheads:
921 ctx = unfi[node]
922 ctx = unfi[node]
922 if ctx.obsolete():
923 if ctx.obsolete():
923 raise error.Abort(mso % ctx)
924 raise error.Abort(mso % ctx)
924 elif ctx.isunstable():
925 elif ctx.isunstable():
925 # TODO print more than one instability in the abort
926 # TODO print more than one instability in the abort
926 # message
927 # message
927 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
928 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
928
929
929 discovery.checkheads(pushop)
930 discovery.checkheads(pushop)
930 return True
931 return True
931
932
932
933
933 # List of names of steps to perform for an outgoing bundle2, order matters.
934 # List of names of steps to perform for an outgoing bundle2, order matters.
934 b2partsgenorder = []
935 b2partsgenorder = []
935
936
936 # Mapping between step name and function
937 # Mapping between step name and function
937 #
938 #
938 # This exists to help extensions wrap steps if necessary
939 # This exists to help extensions wrap steps if necessary
939 b2partsgenmapping = {}
940 b2partsgenmapping = {}
940
941
941
942
942 def b2partsgenerator(stepname, idx=None):
943 def b2partsgenerator(stepname, idx=None):
943 """decorator for function generating bundle2 part
944 """decorator for function generating bundle2 part
944
945
945 The function is added to the step -> function mapping and appended to the
946 The function is added to the step -> function mapping and appended to the
946 list of steps. Beware that decorated functions will be added in order
947 list of steps. Beware that decorated functions will be added in order
947 (this may matter).
948 (this may matter).
948
949
949 You can only use this decorator for new steps, if you want to wrap a step
950 You can only use this decorator for new steps, if you want to wrap a step
950 from an extension, attack the b2partsgenmapping dictionary directly."""
951 from an extension, attack the b2partsgenmapping dictionary directly."""
951
952
952 def dec(func):
953 def dec(func):
953 assert stepname not in b2partsgenmapping
954 assert stepname not in b2partsgenmapping
954 b2partsgenmapping[stepname] = func
955 b2partsgenmapping[stepname] = func
955 if idx is None:
956 if idx is None:
956 b2partsgenorder.append(stepname)
957 b2partsgenorder.append(stepname)
957 else:
958 else:
958 b2partsgenorder.insert(idx, stepname)
959 b2partsgenorder.insert(idx, stepname)
959 return func
960 return func
960
961
961 return dec
962 return dec
962
963
963
964
964 def _pushb2ctxcheckheads(pushop, bundler):
965 def _pushb2ctxcheckheads(pushop, bundler):
965 """Generate race condition checking parts
966 """Generate race condition checking parts
966
967
967 Exists as an independent function to aid extensions
968 Exists as an independent function to aid extensions
968 """
969 """
969 # * 'force' do not check for push race,
970 # * 'force' do not check for push race,
970 # * if we don't push anything, there are nothing to check.
971 # * if we don't push anything, there are nothing to check.
971 if not pushop.force and pushop.outgoing.missingheads:
972 if not pushop.force and pushop.outgoing.missingheads:
972 allowunrelated = b'related' in bundler.capabilities.get(
973 allowunrelated = b'related' in bundler.capabilities.get(
973 b'checkheads', ()
974 b'checkheads', ()
974 )
975 )
975 emptyremote = pushop.pushbranchmap is None
976 emptyremote = pushop.pushbranchmap is None
976 if not allowunrelated or emptyremote:
977 if not allowunrelated or emptyremote:
977 bundler.newpart(b'check:heads', data=iter(pushop.remoteheads))
978 bundler.newpart(b'check:heads', data=iter(pushop.remoteheads))
978 else:
979 else:
979 affected = set()
980 affected = set()
980 for branch, heads in pycompat.iteritems(pushop.pushbranchmap):
981 for branch, heads in pycompat.iteritems(pushop.pushbranchmap):
981 remoteheads, newheads, unsyncedheads, discardedheads = heads
982 remoteheads, newheads, unsyncedheads, discardedheads = heads
982 if remoteheads is not None:
983 if remoteheads is not None:
983 remote = set(remoteheads)
984 remote = set(remoteheads)
984 affected |= set(discardedheads) & remote
985 affected |= set(discardedheads) & remote
985 affected |= remote - set(newheads)
986 affected |= remote - set(newheads)
986 if affected:
987 if affected:
987 data = iter(sorted(affected))
988 data = iter(sorted(affected))
988 bundler.newpart(b'check:updated-heads', data=data)
989 bundler.newpart(b'check:updated-heads', data=data)
989
990
990
991
991 def _pushing(pushop):
992 def _pushing(pushop):
992 """return True if we are pushing anything"""
993 """return True if we are pushing anything"""
993 return bool(
994 return bool(
994 pushop.outgoing.missing
995 pushop.outgoing.missing
995 or pushop.outdatedphases
996 or pushop.outdatedphases
996 or pushop.outobsmarkers
997 or pushop.outobsmarkers
997 or pushop.outbookmarks
998 or pushop.outbookmarks
998 )
999 )
999
1000
1000
1001
1001 @b2partsgenerator(b'check-bookmarks')
1002 @b2partsgenerator(b'check-bookmarks')
1002 def _pushb2checkbookmarks(pushop, bundler):
1003 def _pushb2checkbookmarks(pushop, bundler):
1003 """insert bookmark move checking"""
1004 """insert bookmark move checking"""
1004 if not _pushing(pushop) or pushop.force:
1005 if not _pushing(pushop) or pushop.force:
1005 return
1006 return
1006 b2caps = bundle2.bundle2caps(pushop.remote)
1007 b2caps = bundle2.bundle2caps(pushop.remote)
1007 hasbookmarkcheck = b'bookmarks' in b2caps
1008 hasbookmarkcheck = b'bookmarks' in b2caps
1008 if not (pushop.outbookmarks and hasbookmarkcheck):
1009 if not (pushop.outbookmarks and hasbookmarkcheck):
1009 return
1010 return
1010 data = []
1011 data = []
1011 for book, old, new in pushop.outbookmarks:
1012 for book, old, new in pushop.outbookmarks:
1012 data.append((book, old))
1013 data.append((book, old))
1013 checkdata = bookmod.binaryencode(data)
1014 checkdata = bookmod.binaryencode(data)
1014 bundler.newpart(b'check:bookmarks', data=checkdata)
1015 bundler.newpart(b'check:bookmarks', data=checkdata)
1015
1016
1016
1017
1017 @b2partsgenerator(b'check-phases')
1018 @b2partsgenerator(b'check-phases')
1018 def _pushb2checkphases(pushop, bundler):
1019 def _pushb2checkphases(pushop, bundler):
1019 """insert phase move checking"""
1020 """insert phase move checking"""
1020 if not _pushing(pushop) or pushop.force:
1021 if not _pushing(pushop) or pushop.force:
1021 return
1022 return
1022 b2caps = bundle2.bundle2caps(pushop.remote)
1023 b2caps = bundle2.bundle2caps(pushop.remote)
1023 hasphaseheads = b'heads' in b2caps.get(b'phases', ())
1024 hasphaseheads = b'heads' in b2caps.get(b'phases', ())
1024 if pushop.remotephases is not None and hasphaseheads:
1025 if pushop.remotephases is not None and hasphaseheads:
1025 # check that the remote phase has not changed
1026 # check that the remote phase has not changed
1026 checks = [[] for p in phases.allphases]
1027 checks = [[] for p in phases.allphases]
1027 checks[phases.public].extend(pushop.remotephases.publicheads)
1028 checks[phases.public].extend(pushop.remotephases.publicheads)
1028 checks[phases.draft].extend(pushop.remotephases.draftroots)
1029 checks[phases.draft].extend(pushop.remotephases.draftroots)
1029 if any(checks):
1030 if any(checks):
1030 for nodes in checks:
1031 for nodes in checks:
1031 nodes.sort()
1032 nodes.sort()
1032 checkdata = phases.binaryencode(checks)
1033 checkdata = phases.binaryencode(checks)
1033 bundler.newpart(b'check:phases', data=checkdata)
1034 bundler.newpart(b'check:phases', data=checkdata)
1034
1035
1035
1036
1036 @b2partsgenerator(b'changeset')
1037 @b2partsgenerator(b'changeset')
1037 def _pushb2ctx(pushop, bundler):
1038 def _pushb2ctx(pushop, bundler):
1038 """handle changegroup push through bundle2
1039 """handle changegroup push through bundle2
1039
1040
1040 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
1041 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
1041 """
1042 """
1042 if b'changesets' in pushop.stepsdone:
1043 if b'changesets' in pushop.stepsdone:
1043 return
1044 return
1044 pushop.stepsdone.add(b'changesets')
1045 pushop.stepsdone.add(b'changesets')
1045 # Send known heads to the server for race detection.
1046 # Send known heads to the server for race detection.
1046 if not _pushcheckoutgoing(pushop):
1047 if not _pushcheckoutgoing(pushop):
1047 return
1048 return
1048 pushop.repo.prepushoutgoinghooks(pushop)
1049 pushop.repo.prepushoutgoinghooks(pushop)
1049
1050
1050 _pushb2ctxcheckheads(pushop, bundler)
1051 _pushb2ctxcheckheads(pushop, bundler)
1051
1052
1052 b2caps = bundle2.bundle2caps(pushop.remote)
1053 b2caps = bundle2.bundle2caps(pushop.remote)
1053 version = b'01'
1054 version = b'01'
1054 cgversions = b2caps.get(b'changegroup')
1055 cgversions = b2caps.get(b'changegroup')
1055 if cgversions: # 3.1 and 3.2 ship with an empty value
1056 if cgversions: # 3.1 and 3.2 ship with an empty value
1056 cgversions = [
1057 cgversions = [
1057 v
1058 v
1058 for v in cgversions
1059 for v in cgversions
1059 if v in changegroup.supportedoutgoingversions(pushop.repo)
1060 if v in changegroup.supportedoutgoingversions(pushop.repo)
1060 ]
1061 ]
1061 if not cgversions:
1062 if not cgversions:
1062 raise error.Abort(_(b'no common changegroup version'))
1063 raise error.Abort(_(b'no common changegroup version'))
1063 version = max(cgversions)
1064 version = max(cgversions)
1064 cgstream = changegroup.makestream(
1065 cgstream = changegroup.makestream(
1065 pushop.repo, pushop.outgoing, version, b'push'
1066 pushop.repo, pushop.outgoing, version, b'push'
1066 )
1067 )
1067 cgpart = bundler.newpart(b'changegroup', data=cgstream)
1068 cgpart = bundler.newpart(b'changegroup', data=cgstream)
1068 if cgversions:
1069 if cgversions:
1069 cgpart.addparam(b'version', version)
1070 cgpart.addparam(b'version', version)
1070 if b'treemanifest' in pushop.repo.requirements:
1071 if b'treemanifest' in pushop.repo.requirements:
1071 cgpart.addparam(b'treemanifest', b'1')
1072 cgpart.addparam(b'treemanifest', b'1')
1072 if b'exp-sidedata-flag' in pushop.repo.requirements:
1073 if b'exp-sidedata-flag' in pushop.repo.requirements:
1073 cgpart.addparam(b'exp-sidedata', b'1')
1074 cgpart.addparam(b'exp-sidedata', b'1')
1074
1075
1075 def handlereply(op):
1076 def handlereply(op):
1076 """extract addchangegroup returns from server reply"""
1077 """extract addchangegroup returns from server reply"""
1077 cgreplies = op.records.getreplies(cgpart.id)
1078 cgreplies = op.records.getreplies(cgpart.id)
1078 assert len(cgreplies[b'changegroup']) == 1
1079 assert len(cgreplies[b'changegroup']) == 1
1079 pushop.cgresult = cgreplies[b'changegroup'][0][b'return']
1080 pushop.cgresult = cgreplies[b'changegroup'][0][b'return']
1080
1081
1081 return handlereply
1082 return handlereply
1082
1083
1083
1084
1084 @b2partsgenerator(b'phase')
1085 @b2partsgenerator(b'phase')
1085 def _pushb2phases(pushop, bundler):
1086 def _pushb2phases(pushop, bundler):
1086 """handle phase push through bundle2"""
1087 """handle phase push through bundle2"""
1087 if b'phases' in pushop.stepsdone:
1088 if b'phases' in pushop.stepsdone:
1088 return
1089 return
1089 b2caps = bundle2.bundle2caps(pushop.remote)
1090 b2caps = bundle2.bundle2caps(pushop.remote)
1090 ui = pushop.repo.ui
1091 ui = pushop.repo.ui
1091
1092
1092 legacyphase = b'phases' in ui.configlist(b'devel', b'legacy.exchange')
1093 legacyphase = b'phases' in ui.configlist(b'devel', b'legacy.exchange')
1093 haspushkey = b'pushkey' in b2caps
1094 haspushkey = b'pushkey' in b2caps
1094 hasphaseheads = b'heads' in b2caps.get(b'phases', ())
1095 hasphaseheads = b'heads' in b2caps.get(b'phases', ())
1095
1096
1096 if hasphaseheads and not legacyphase:
1097 if hasphaseheads and not legacyphase:
1097 return _pushb2phaseheads(pushop, bundler)
1098 return _pushb2phaseheads(pushop, bundler)
1098 elif haspushkey:
1099 elif haspushkey:
1099 return _pushb2phasespushkey(pushop, bundler)
1100 return _pushb2phasespushkey(pushop, bundler)
1100
1101
1101
1102
1102 def _pushb2phaseheads(pushop, bundler):
1103 def _pushb2phaseheads(pushop, bundler):
1103 """push phase information through a bundle2 - binary part"""
1104 """push phase information through a bundle2 - binary part"""
1104 pushop.stepsdone.add(b'phases')
1105 pushop.stepsdone.add(b'phases')
1105 if pushop.outdatedphases:
1106 if pushop.outdatedphases:
1106 updates = [[] for p in phases.allphases]
1107 updates = [[] for p in phases.allphases]
1107 updates[0].extend(h.node() for h in pushop.outdatedphases)
1108 updates[0].extend(h.node() for h in pushop.outdatedphases)
1108 phasedata = phases.binaryencode(updates)
1109 phasedata = phases.binaryencode(updates)
1109 bundler.newpart(b'phase-heads', data=phasedata)
1110 bundler.newpart(b'phase-heads', data=phasedata)
1110
1111
1111
1112
1112 def _pushb2phasespushkey(pushop, bundler):
1113 def _pushb2phasespushkey(pushop, bundler):
1113 """push phase information through a bundle2 - pushkey part"""
1114 """push phase information through a bundle2 - pushkey part"""
1114 pushop.stepsdone.add(b'phases')
1115 pushop.stepsdone.add(b'phases')
1115 part2node = []
1116 part2node = []
1116
1117
1117 def handlefailure(pushop, exc):
1118 def handlefailure(pushop, exc):
1118 targetid = int(exc.partid)
1119 targetid = int(exc.partid)
1119 for partid, node in part2node:
1120 for partid, node in part2node:
1120 if partid == targetid:
1121 if partid == targetid:
1121 raise error.Abort(_(b'updating %s to public failed') % node)
1122 raise error.Abort(_(b'updating %s to public failed') % node)
1122
1123
1123 enc = pushkey.encode
1124 enc = pushkey.encode
1124 for newremotehead in pushop.outdatedphases:
1125 for newremotehead in pushop.outdatedphases:
1125 part = bundler.newpart(b'pushkey')
1126 part = bundler.newpart(b'pushkey')
1126 part.addparam(b'namespace', enc(b'phases'))
1127 part.addparam(b'namespace', enc(b'phases'))
1127 part.addparam(b'key', enc(newremotehead.hex()))
1128 part.addparam(b'key', enc(newremotehead.hex()))
1128 part.addparam(b'old', enc(b'%d' % phases.draft))
1129 part.addparam(b'old', enc(b'%d' % phases.draft))
1129 part.addparam(b'new', enc(b'%d' % phases.public))
1130 part.addparam(b'new', enc(b'%d' % phases.public))
1130 part2node.append((part.id, newremotehead))
1131 part2node.append((part.id, newremotehead))
1131 pushop.pkfailcb[part.id] = handlefailure
1132 pushop.pkfailcb[part.id] = handlefailure
1132
1133
1133 def handlereply(op):
1134 def handlereply(op):
1134 for partid, node in part2node:
1135 for partid, node in part2node:
1135 partrep = op.records.getreplies(partid)
1136 partrep = op.records.getreplies(partid)
1136 results = partrep[b'pushkey']
1137 results = partrep[b'pushkey']
1137 assert len(results) <= 1
1138 assert len(results) <= 1
1138 msg = None
1139 msg = None
1139 if not results:
1140 if not results:
1140 msg = _(b'server ignored update of %s to public!\n') % node
1141 msg = _(b'server ignored update of %s to public!\n') % node
1141 elif not int(results[0][b'return']):
1142 elif not int(results[0][b'return']):
1142 msg = _(b'updating %s to public failed!\n') % node
1143 msg = _(b'updating %s to public failed!\n') % node
1143 if msg is not None:
1144 if msg is not None:
1144 pushop.ui.warn(msg)
1145 pushop.ui.warn(msg)
1145
1146
1146 return handlereply
1147 return handlereply
1147
1148
1148
1149
1149 @b2partsgenerator(b'obsmarkers')
1150 @b2partsgenerator(b'obsmarkers')
1150 def _pushb2obsmarkers(pushop, bundler):
1151 def _pushb2obsmarkers(pushop, bundler):
1151 if b'obsmarkers' in pushop.stepsdone:
1152 if b'obsmarkers' in pushop.stepsdone:
1152 return
1153 return
1153 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
1154 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
1154 if obsolete.commonversion(remoteversions) is None:
1155 if obsolete.commonversion(remoteversions) is None:
1155 return
1156 return
1156 pushop.stepsdone.add(b'obsmarkers')
1157 pushop.stepsdone.add(b'obsmarkers')
1157 if pushop.outobsmarkers:
1158 if pushop.outobsmarkers:
1158 markers = obsutil.sortedmarkers(pushop.outobsmarkers)
1159 markers = obsutil.sortedmarkers(pushop.outobsmarkers)
1159 bundle2.buildobsmarkerspart(bundler, markers)
1160 bundle2.buildobsmarkerspart(bundler, markers)
1160
1161
1161
1162
1162 @b2partsgenerator(b'bookmarks')
1163 @b2partsgenerator(b'bookmarks')
1163 def _pushb2bookmarks(pushop, bundler):
1164 def _pushb2bookmarks(pushop, bundler):
1164 """handle bookmark push through bundle2"""
1165 """handle bookmark push through bundle2"""
1165 if b'bookmarks' in pushop.stepsdone:
1166 if b'bookmarks' in pushop.stepsdone:
1166 return
1167 return
1167 b2caps = bundle2.bundle2caps(pushop.remote)
1168 b2caps = bundle2.bundle2caps(pushop.remote)
1168
1169
1169 legacy = pushop.repo.ui.configlist(b'devel', b'legacy.exchange')
1170 legacy = pushop.repo.ui.configlist(b'devel', b'legacy.exchange')
1170 legacybooks = b'bookmarks' in legacy
1171 legacybooks = b'bookmarks' in legacy
1171
1172
1172 if not legacybooks and b'bookmarks' in b2caps:
1173 if not legacybooks and b'bookmarks' in b2caps:
1173 return _pushb2bookmarkspart(pushop, bundler)
1174 return _pushb2bookmarkspart(pushop, bundler)
1174 elif b'pushkey' in b2caps:
1175 elif b'pushkey' in b2caps:
1175 return _pushb2bookmarkspushkey(pushop, bundler)
1176 return _pushb2bookmarkspushkey(pushop, bundler)
1176
1177
1177
1178
1178 def _bmaction(old, new):
1179 def _bmaction(old, new):
1179 """small utility for bookmark pushing"""
1180 """small utility for bookmark pushing"""
1180 if not old:
1181 if not old:
1181 return b'export'
1182 return b'export'
1182 elif not new:
1183 elif not new:
1183 return b'delete'
1184 return b'delete'
1184 return b'update'
1185 return b'update'
1185
1186
1186
1187
1187 def _abortonsecretctx(pushop, node, b):
1188 def _abortonsecretctx(pushop, node, b):
1188 """abort if a given bookmark points to a secret changeset"""
1189 """abort if a given bookmark points to a secret changeset"""
1189 if node and pushop.repo[node].phase() == phases.secret:
1190 if node and pushop.repo[node].phase() == phases.secret:
1190 raise error.Abort(
1191 raise error.Abort(
1191 _(b'cannot push bookmark %s as it points to a secret changeset') % b
1192 _(b'cannot push bookmark %s as it points to a secret changeset') % b
1192 )
1193 )
1193
1194
1194
1195
1195 def _pushb2bookmarkspart(pushop, bundler):
1196 def _pushb2bookmarkspart(pushop, bundler):
1196 pushop.stepsdone.add(b'bookmarks')
1197 pushop.stepsdone.add(b'bookmarks')
1197 if not pushop.outbookmarks:
1198 if not pushop.outbookmarks:
1198 return
1199 return
1199
1200
1200 allactions = []
1201 allactions = []
1201 data = []
1202 data = []
1202 for book, old, new in pushop.outbookmarks:
1203 for book, old, new in pushop.outbookmarks:
1203 _abortonsecretctx(pushop, new, book)
1204 _abortonsecretctx(pushop, new, book)
1204 data.append((book, new))
1205 data.append((book, new))
1205 allactions.append((book, _bmaction(old, new)))
1206 allactions.append((book, _bmaction(old, new)))
1206 checkdata = bookmod.binaryencode(data)
1207 checkdata = bookmod.binaryencode(data)
1207 bundler.newpart(b'bookmarks', data=checkdata)
1208 bundler.newpart(b'bookmarks', data=checkdata)
1208
1209
1209 def handlereply(op):
1210 def handlereply(op):
1210 ui = pushop.ui
1211 ui = pushop.ui
1211 # if success
1212 # if success
1212 for book, action in allactions:
1213 for book, action in allactions:
1213 ui.status(bookmsgmap[action][0] % book)
1214 ui.status(bookmsgmap[action][0] % book)
1214
1215
1215 return handlereply
1216 return handlereply
1216
1217
1217
1218
1218 def _pushb2bookmarkspushkey(pushop, bundler):
1219 def _pushb2bookmarkspushkey(pushop, bundler):
1219 pushop.stepsdone.add(b'bookmarks')
1220 pushop.stepsdone.add(b'bookmarks')
1220 part2book = []
1221 part2book = []
1221 enc = pushkey.encode
1222 enc = pushkey.encode
1222
1223
1223 def handlefailure(pushop, exc):
1224 def handlefailure(pushop, exc):
1224 targetid = int(exc.partid)
1225 targetid = int(exc.partid)
1225 for partid, book, action in part2book:
1226 for partid, book, action in part2book:
1226 if partid == targetid:
1227 if partid == targetid:
1227 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
1228 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
1228 # we should not be called for part we did not generated
1229 # we should not be called for part we did not generated
1229 assert False
1230 assert False
1230
1231
1231 for book, old, new in pushop.outbookmarks:
1232 for book, old, new in pushop.outbookmarks:
1232 _abortonsecretctx(pushop, new, book)
1233 _abortonsecretctx(pushop, new, book)
1233 part = bundler.newpart(b'pushkey')
1234 part = bundler.newpart(b'pushkey')
1234 part.addparam(b'namespace', enc(b'bookmarks'))
1235 part.addparam(b'namespace', enc(b'bookmarks'))
1235 part.addparam(b'key', enc(book))
1236 part.addparam(b'key', enc(book))
1236 part.addparam(b'old', enc(hex(old)))
1237 part.addparam(b'old', enc(hex(old)))
1237 part.addparam(b'new', enc(hex(new)))
1238 part.addparam(b'new', enc(hex(new)))
1238 action = b'update'
1239 action = b'update'
1239 if not old:
1240 if not old:
1240 action = b'export'
1241 action = b'export'
1241 elif not new:
1242 elif not new:
1242 action = b'delete'
1243 action = b'delete'
1243 part2book.append((part.id, book, action))
1244 part2book.append((part.id, book, action))
1244 pushop.pkfailcb[part.id] = handlefailure
1245 pushop.pkfailcb[part.id] = handlefailure
1245
1246
1246 def handlereply(op):
1247 def handlereply(op):
1247 ui = pushop.ui
1248 ui = pushop.ui
1248 for partid, book, action in part2book:
1249 for partid, book, action in part2book:
1249 partrep = op.records.getreplies(partid)
1250 partrep = op.records.getreplies(partid)
1250 results = partrep[b'pushkey']
1251 results = partrep[b'pushkey']
1251 assert len(results) <= 1
1252 assert len(results) <= 1
1252 if not results:
1253 if not results:
1253 pushop.ui.warn(_(b'server ignored bookmark %s update\n') % book)
1254 pushop.ui.warn(_(b'server ignored bookmark %s update\n') % book)
1254 else:
1255 else:
1255 ret = int(results[0][b'return'])
1256 ret = int(results[0][b'return'])
1256 if ret:
1257 if ret:
1257 ui.status(bookmsgmap[action][0] % book)
1258 ui.status(bookmsgmap[action][0] % book)
1258 else:
1259 else:
1259 ui.warn(bookmsgmap[action][1] % book)
1260 ui.warn(bookmsgmap[action][1] % book)
1260 if pushop.bkresult is not None:
1261 if pushop.bkresult is not None:
1261 pushop.bkresult = 1
1262 pushop.bkresult = 1
1262
1263
1263 return handlereply
1264 return handlereply
1264
1265
1265
1266
1266 @b2partsgenerator(b'pushvars', idx=0)
1267 @b2partsgenerator(b'pushvars', idx=0)
1267 def _getbundlesendvars(pushop, bundler):
1268 def _getbundlesendvars(pushop, bundler):
1268 '''send shellvars via bundle2'''
1269 '''send shellvars via bundle2'''
1269 pushvars = pushop.pushvars
1270 pushvars = pushop.pushvars
1270 if pushvars:
1271 if pushvars:
1271 shellvars = {}
1272 shellvars = {}
1272 for raw in pushvars:
1273 for raw in pushvars:
1273 if b'=' not in raw:
1274 if b'=' not in raw:
1274 msg = (
1275 msg = (
1275 b"unable to parse variable '%s', should follow "
1276 b"unable to parse variable '%s', should follow "
1276 b"'KEY=VALUE' or 'KEY=' format"
1277 b"'KEY=VALUE' or 'KEY=' format"
1277 )
1278 )
1278 raise error.Abort(msg % raw)
1279 raise error.Abort(msg % raw)
1279 k, v = raw.split(b'=', 1)
1280 k, v = raw.split(b'=', 1)
1280 shellvars[k] = v
1281 shellvars[k] = v
1281
1282
1282 part = bundler.newpart(b'pushvars')
1283 part = bundler.newpart(b'pushvars')
1283
1284
1284 for key, value in pycompat.iteritems(shellvars):
1285 for key, value in pycompat.iteritems(shellvars):
1285 part.addparam(key, value, mandatory=False)
1286 part.addparam(key, value, mandatory=False)
1286
1287
1287
1288
1288 def _pushbundle2(pushop):
1289 def _pushbundle2(pushop):
1289 """push data to the remote using bundle2
1290 """push data to the remote using bundle2
1290
1291
1291 The only currently supported type of data is changegroup but this will
1292 The only currently supported type of data is changegroup but this will
1292 evolve in the future."""
1293 evolve in the future."""
1293 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
1294 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
1294 pushback = pushop.trmanager and pushop.ui.configbool(
1295 pushback = pushop.trmanager and pushop.ui.configbool(
1295 b'experimental', b'bundle2.pushback'
1296 b'experimental', b'bundle2.pushback'
1296 )
1297 )
1297
1298
1298 # create reply capability
1299 # create reply capability
1299 capsblob = bundle2.encodecaps(
1300 capsblob = bundle2.encodecaps(
1300 bundle2.getrepocaps(pushop.repo, allowpushback=pushback, role=b'client')
1301 bundle2.getrepocaps(pushop.repo, allowpushback=pushback, role=b'client')
1301 )
1302 )
1302 bundler.newpart(b'replycaps', data=capsblob)
1303 bundler.newpart(b'replycaps', data=capsblob)
1303 replyhandlers = []
1304 replyhandlers = []
1304 for partgenname in b2partsgenorder:
1305 for partgenname in b2partsgenorder:
1305 partgen = b2partsgenmapping[partgenname]
1306 partgen = b2partsgenmapping[partgenname]
1306 ret = partgen(pushop, bundler)
1307 ret = partgen(pushop, bundler)
1307 if callable(ret):
1308 if callable(ret):
1308 replyhandlers.append(ret)
1309 replyhandlers.append(ret)
1309 # do not push if nothing to push
1310 # do not push if nothing to push
1310 if bundler.nbparts <= 1:
1311 if bundler.nbparts <= 1:
1311 return
1312 return
1312 stream = util.chunkbuffer(bundler.getchunks())
1313 stream = util.chunkbuffer(bundler.getchunks())
1313 try:
1314 try:
1314 try:
1315 try:
1315 with pushop.remote.commandexecutor() as e:
1316 with pushop.remote.commandexecutor() as e:
1316 reply = e.callcommand(
1317 reply = e.callcommand(
1317 b'unbundle',
1318 b'unbundle',
1318 {
1319 {
1319 b'bundle': stream,
1320 b'bundle': stream,
1320 b'heads': [b'force'],
1321 b'heads': [b'force'],
1321 b'url': pushop.remote.url(),
1322 b'url': pushop.remote.url(),
1322 },
1323 },
1323 ).result()
1324 ).result()
1324 except error.BundleValueError as exc:
1325 except error.BundleValueError as exc:
1325 raise error.Abort(_(b'missing support for %s') % exc)
1326 raise error.Abort(_(b'missing support for %s') % exc)
1326 try:
1327 try:
1327 trgetter = None
1328 trgetter = None
1328 if pushback:
1329 if pushback:
1329 trgetter = pushop.trmanager.transaction
1330 trgetter = pushop.trmanager.transaction
1330 op = bundle2.processbundle(pushop.repo, reply, trgetter)
1331 op = bundle2.processbundle(pushop.repo, reply, trgetter)
1331 except error.BundleValueError as exc:
1332 except error.BundleValueError as exc:
1332 raise error.Abort(_(b'missing support for %s') % exc)
1333 raise error.Abort(_(b'missing support for %s') % exc)
1333 except bundle2.AbortFromPart as exc:
1334 except bundle2.AbortFromPart as exc:
1334 pushop.ui.status(_(b'remote: %s\n') % exc)
1335 pushop.ui.status(_(b'remote: %s\n') % exc)
1335 if exc.hint is not None:
1336 if exc.hint is not None:
1336 pushop.ui.status(_(b'remote: %s\n') % (b'(%s)' % exc.hint))
1337 pushop.ui.status(_(b'remote: %s\n') % (b'(%s)' % exc.hint))
1337 raise error.Abort(_(b'push failed on remote'))
1338 raise error.Abort(_(b'push failed on remote'))
1338 except error.PushkeyFailed as exc:
1339 except error.PushkeyFailed as exc:
1339 partid = int(exc.partid)
1340 partid = int(exc.partid)
1340 if partid not in pushop.pkfailcb:
1341 if partid not in pushop.pkfailcb:
1341 raise
1342 raise
1342 pushop.pkfailcb[partid](pushop, exc)
1343 pushop.pkfailcb[partid](pushop, exc)
1343 for rephand in replyhandlers:
1344 for rephand in replyhandlers:
1344 rephand(op)
1345 rephand(op)
1345
1346
1346
1347
1347 def _pushchangeset(pushop):
1348 def _pushchangeset(pushop):
1348 """Make the actual push of changeset bundle to remote repo"""
1349 """Make the actual push of changeset bundle to remote repo"""
1349 if b'changesets' in pushop.stepsdone:
1350 if b'changesets' in pushop.stepsdone:
1350 return
1351 return
1351 pushop.stepsdone.add(b'changesets')
1352 pushop.stepsdone.add(b'changesets')
1352 if not _pushcheckoutgoing(pushop):
1353 if not _pushcheckoutgoing(pushop):
1353 return
1354 return
1354
1355
1355 # Should have verified this in push().
1356 # Should have verified this in push().
1356 assert pushop.remote.capable(b'unbundle')
1357 assert pushop.remote.capable(b'unbundle')
1357
1358
1358 pushop.repo.prepushoutgoinghooks(pushop)
1359 pushop.repo.prepushoutgoinghooks(pushop)
1359 outgoing = pushop.outgoing
1360 outgoing = pushop.outgoing
1360 # TODO: get bundlecaps from remote
1361 # TODO: get bundlecaps from remote
1361 bundlecaps = None
1362 bundlecaps = None
1362 # create a changegroup from local
1363 # create a changegroup from local
1363 if pushop.revs is None and not (
1364 if pushop.revs is None and not (
1364 outgoing.excluded or pushop.repo.changelog.filteredrevs
1365 outgoing.excluded or pushop.repo.changelog.filteredrevs
1365 ):
1366 ):
1366 # push everything,
1367 # push everything,
1367 # use the fast path, no race possible on push
1368 # use the fast path, no race possible on push
1368 cg = changegroup.makechangegroup(
1369 cg = changegroup.makechangegroup(
1369 pushop.repo,
1370 pushop.repo,
1370 outgoing,
1371 outgoing,
1371 b'01',
1372 b'01',
1372 b'push',
1373 b'push',
1373 fastpath=True,
1374 fastpath=True,
1374 bundlecaps=bundlecaps,
1375 bundlecaps=bundlecaps,
1375 )
1376 )
1376 else:
1377 else:
1377 cg = changegroup.makechangegroup(
1378 cg = changegroup.makechangegroup(
1378 pushop.repo, outgoing, b'01', b'push', bundlecaps=bundlecaps
1379 pushop.repo, outgoing, b'01', b'push', bundlecaps=bundlecaps
1379 )
1380 )
1380
1381
1381 # apply changegroup to remote
1382 # apply changegroup to remote
1382 # local repo finds heads on server, finds out what
1383 # local repo finds heads on server, finds out what
1383 # revs it must push. once revs transferred, if server
1384 # revs it must push. once revs transferred, if server
1384 # finds it has different heads (someone else won
1385 # finds it has different heads (someone else won
1385 # commit/push race), server aborts.
1386 # commit/push race), server aborts.
1386 if pushop.force:
1387 if pushop.force:
1387 remoteheads = [b'force']
1388 remoteheads = [b'force']
1388 else:
1389 else:
1389 remoteheads = pushop.remoteheads
1390 remoteheads = pushop.remoteheads
1390 # ssh: return remote's addchangegroup()
1391 # ssh: return remote's addchangegroup()
1391 # http: return remote's addchangegroup() or 0 for error
1392 # http: return remote's addchangegroup() or 0 for error
1392 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads, pushop.repo.url())
1393 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads, pushop.repo.url())
1393
1394
1394
1395
1395 def _pushsyncphase(pushop):
1396 def _pushsyncphase(pushop):
1396 """synchronise phase information locally and remotely"""
1397 """synchronise phase information locally and remotely"""
1397 cheads = pushop.commonheads
1398 cheads = pushop.commonheads
1398 # even when we don't push, exchanging phase data is useful
1399 # even when we don't push, exchanging phase data is useful
1399 remotephases = listkeys(pushop.remote, b'phases')
1400 remotephases = listkeys(pushop.remote, b'phases')
1400 if (
1401 if (
1401 pushop.ui.configbool(b'ui', b'_usedassubrepo')
1402 pushop.ui.configbool(b'ui', b'_usedassubrepo')
1402 and remotephases # server supports phases
1403 and remotephases # server supports phases
1403 and pushop.cgresult is None # nothing was pushed
1404 and pushop.cgresult is None # nothing was pushed
1404 and remotephases.get(b'publishing', False)
1405 and remotephases.get(b'publishing', False)
1405 ):
1406 ):
1406 # When:
1407 # When:
1407 # - this is a subrepo push
1408 # - this is a subrepo push
1408 # - and remote support phase
1409 # - and remote support phase
1409 # - and no changeset was pushed
1410 # - and no changeset was pushed
1410 # - and remote is publishing
1411 # - and remote is publishing
1411 # We may be in issue 3871 case!
1412 # We may be in issue 3871 case!
1412 # We drop the possible phase synchronisation done by
1413 # We drop the possible phase synchronisation done by
1413 # courtesy to publish changesets possibly locally draft
1414 # courtesy to publish changesets possibly locally draft
1414 # on the remote.
1415 # on the remote.
1415 remotephases = {b'publishing': b'True'}
1416 remotephases = {b'publishing': b'True'}
1416 if not remotephases: # old server or public only reply from non-publishing
1417 if not remotephases: # old server or public only reply from non-publishing
1417 _localphasemove(pushop, cheads)
1418 _localphasemove(pushop, cheads)
1418 # don't push any phase data as there is nothing to push
1419 # don't push any phase data as there is nothing to push
1419 else:
1420 else:
1420 ana = phases.analyzeremotephases(pushop.repo, cheads, remotephases)
1421 ana = phases.analyzeremotephases(pushop.repo, cheads, remotephases)
1421 pheads, droots = ana
1422 pheads, droots = ana
1422 ### Apply remote phase on local
1423 ### Apply remote phase on local
1423 if remotephases.get(b'publishing', False):
1424 if remotephases.get(b'publishing', False):
1424 _localphasemove(pushop, cheads)
1425 _localphasemove(pushop, cheads)
1425 else: # publish = False
1426 else: # publish = False
1426 _localphasemove(pushop, pheads)
1427 _localphasemove(pushop, pheads)
1427 _localphasemove(pushop, cheads, phases.draft)
1428 _localphasemove(pushop, cheads, phases.draft)
1428 ### Apply local phase on remote
1429 ### Apply local phase on remote
1429
1430
1430 if pushop.cgresult:
1431 if pushop.cgresult:
1431 if b'phases' in pushop.stepsdone:
1432 if b'phases' in pushop.stepsdone:
1432 # phases already pushed though bundle2
1433 # phases already pushed though bundle2
1433 return
1434 return
1434 outdated = pushop.outdatedphases
1435 outdated = pushop.outdatedphases
1435 else:
1436 else:
1436 outdated = pushop.fallbackoutdatedphases
1437 outdated = pushop.fallbackoutdatedphases
1437
1438
1438 pushop.stepsdone.add(b'phases')
1439 pushop.stepsdone.add(b'phases')
1439
1440
1440 # filter heads already turned public by the push
1441 # filter heads already turned public by the push
1441 outdated = [c for c in outdated if c.node() not in pheads]
1442 outdated = [c for c in outdated if c.node() not in pheads]
1442 # fallback to independent pushkey command
1443 # fallback to independent pushkey command
1443 for newremotehead in outdated:
1444 for newremotehead in outdated:
1444 with pushop.remote.commandexecutor() as e:
1445 with pushop.remote.commandexecutor() as e:
1445 r = e.callcommand(
1446 r = e.callcommand(
1446 b'pushkey',
1447 b'pushkey',
1447 {
1448 {
1448 b'namespace': b'phases',
1449 b'namespace': b'phases',
1449 b'key': newremotehead.hex(),
1450 b'key': newremotehead.hex(),
1450 b'old': b'%d' % phases.draft,
1451 b'old': b'%d' % phases.draft,
1451 b'new': b'%d' % phases.public,
1452 b'new': b'%d' % phases.public,
1452 },
1453 },
1453 ).result()
1454 ).result()
1454
1455
1455 if not r:
1456 if not r:
1456 pushop.ui.warn(
1457 pushop.ui.warn(
1457 _(b'updating %s to public failed!\n') % newremotehead
1458 _(b'updating %s to public failed!\n') % newremotehead
1458 )
1459 )
1459
1460
1460
1461
1461 def _localphasemove(pushop, nodes, phase=phases.public):
1462 def _localphasemove(pushop, nodes, phase=phases.public):
1462 """move <nodes> to <phase> in the local source repo"""
1463 """move <nodes> to <phase> in the local source repo"""
1463 if pushop.trmanager:
1464 if pushop.trmanager:
1464 phases.advanceboundary(
1465 phases.advanceboundary(
1465 pushop.repo, pushop.trmanager.transaction(), phase, nodes
1466 pushop.repo, pushop.trmanager.transaction(), phase, nodes
1466 )
1467 )
1467 else:
1468 else:
1468 # repo is not locked, do not change any phases!
1469 # repo is not locked, do not change any phases!
1469 # Informs the user that phases should have been moved when
1470 # Informs the user that phases should have been moved when
1470 # applicable.
1471 # applicable.
1471 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1472 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1472 phasestr = phases.phasenames[phase]
1473 phasestr = phases.phasenames[phase]
1473 if actualmoves:
1474 if actualmoves:
1474 pushop.ui.status(
1475 pushop.ui.status(
1475 _(
1476 _(
1476 b'cannot lock source repo, skipping '
1477 b'cannot lock source repo, skipping '
1477 b'local %s phase update\n'
1478 b'local %s phase update\n'
1478 )
1479 )
1479 % phasestr
1480 % phasestr
1480 )
1481 )
1481
1482
1482
1483
1483 def _pushobsolete(pushop):
1484 def _pushobsolete(pushop):
1484 """utility function to push obsolete markers to a remote"""
1485 """utility function to push obsolete markers to a remote"""
1485 if b'obsmarkers' in pushop.stepsdone:
1486 if b'obsmarkers' in pushop.stepsdone:
1486 return
1487 return
1487 repo = pushop.repo
1488 repo = pushop.repo
1488 remote = pushop.remote
1489 remote = pushop.remote
1489 pushop.stepsdone.add(b'obsmarkers')
1490 pushop.stepsdone.add(b'obsmarkers')
1490 if pushop.outobsmarkers:
1491 if pushop.outobsmarkers:
1491 pushop.ui.debug(b'try to push obsolete markers to remote\n')
1492 pushop.ui.debug(b'try to push obsolete markers to remote\n')
1492 rslts = []
1493 rslts = []
1493 markers = obsutil.sortedmarkers(pushop.outobsmarkers)
1494 markers = obsutil.sortedmarkers(pushop.outobsmarkers)
1494 remotedata = obsolete._pushkeyescape(markers)
1495 remotedata = obsolete._pushkeyescape(markers)
1495 for key in sorted(remotedata, reverse=True):
1496 for key in sorted(remotedata, reverse=True):
1496 # reverse sort to ensure we end with dump0
1497 # reverse sort to ensure we end with dump0
1497 data = remotedata[key]
1498 data = remotedata[key]
1498 rslts.append(remote.pushkey(b'obsolete', key, b'', data))
1499 rslts.append(remote.pushkey(b'obsolete', key, b'', data))
1499 if [r for r in rslts if not r]:
1500 if [r for r in rslts if not r]:
1500 msg = _(b'failed to push some obsolete markers!\n')
1501 msg = _(b'failed to push some obsolete markers!\n')
1501 repo.ui.warn(msg)
1502 repo.ui.warn(msg)
1502
1503
1503
1504
1504 def _pushbookmark(pushop):
1505 def _pushbookmark(pushop):
1505 """Update bookmark position on remote"""
1506 """Update bookmark position on remote"""
1506 if pushop.cgresult == 0 or b'bookmarks' in pushop.stepsdone:
1507 if pushop.cgresult == 0 or b'bookmarks' in pushop.stepsdone:
1507 return
1508 return
1508 pushop.stepsdone.add(b'bookmarks')
1509 pushop.stepsdone.add(b'bookmarks')
1509 ui = pushop.ui
1510 ui = pushop.ui
1510 remote = pushop.remote
1511 remote = pushop.remote
1511
1512
1512 for b, old, new in pushop.outbookmarks:
1513 for b, old, new in pushop.outbookmarks:
1513 action = b'update'
1514 action = b'update'
1514 if not old:
1515 if not old:
1515 action = b'export'
1516 action = b'export'
1516 elif not new:
1517 elif not new:
1517 action = b'delete'
1518 action = b'delete'
1518
1519
1519 with remote.commandexecutor() as e:
1520 with remote.commandexecutor() as e:
1520 r = e.callcommand(
1521 r = e.callcommand(
1521 b'pushkey',
1522 b'pushkey',
1522 {
1523 {
1523 b'namespace': b'bookmarks',
1524 b'namespace': b'bookmarks',
1524 b'key': b,
1525 b'key': b,
1525 b'old': hex(old),
1526 b'old': hex(old),
1526 b'new': hex(new),
1527 b'new': hex(new),
1527 },
1528 },
1528 ).result()
1529 ).result()
1529
1530
1530 if r:
1531 if r:
1531 ui.status(bookmsgmap[action][0] % b)
1532 ui.status(bookmsgmap[action][0] % b)
1532 else:
1533 else:
1533 ui.warn(bookmsgmap[action][1] % b)
1534 ui.warn(bookmsgmap[action][1] % b)
1534 # discovery can have set the value form invalid entry
1535 # discovery can have set the value form invalid entry
1535 if pushop.bkresult is not None:
1536 if pushop.bkresult is not None:
1536 pushop.bkresult = 1
1537 pushop.bkresult = 1
1537
1538
1538
1539
1539 class pulloperation(object):
1540 class pulloperation(object):
1540 """A object that represent a single pull operation
1541 """A object that represent a single pull operation
1541
1542
1542 It purpose is to carry pull related state and very common operation.
1543 It purpose is to carry pull related state and very common operation.
1543
1544
1544 A new should be created at the beginning of each pull and discarded
1545 A new should be created at the beginning of each pull and discarded
1545 afterward.
1546 afterward.
1546 """
1547 """
1547
1548
1548 def __init__(
1549 def __init__(
1549 self,
1550 self,
1550 repo,
1551 repo,
1551 remote,
1552 remote,
1552 heads=None,
1553 heads=None,
1553 force=False,
1554 force=False,
1554 bookmarks=(),
1555 bookmarks=(),
1555 remotebookmarks=None,
1556 remotebookmarks=None,
1556 streamclonerequested=None,
1557 streamclonerequested=None,
1557 includepats=None,
1558 includepats=None,
1558 excludepats=None,
1559 excludepats=None,
1559 depth=None,
1560 depth=None,
1560 ):
1561 ):
1561 # repo we pull into
1562 # repo we pull into
1562 self.repo = repo
1563 self.repo = repo
1563 # repo we pull from
1564 # repo we pull from
1564 self.remote = remote
1565 self.remote = remote
1565 # revision we try to pull (None is "all")
1566 # revision we try to pull (None is "all")
1566 self.heads = heads
1567 self.heads = heads
1567 # bookmark pulled explicitly
1568 # bookmark pulled explicitly
1568 self.explicitbookmarks = [
1569 self.explicitbookmarks = [
1569 repo._bookmarks.expandname(bookmark) for bookmark in bookmarks
1570 repo._bookmarks.expandname(bookmark) for bookmark in bookmarks
1570 ]
1571 ]
1571 # do we force pull?
1572 # do we force pull?
1572 self.force = force
1573 self.force = force
1573 # whether a streaming clone was requested
1574 # whether a streaming clone was requested
1574 self.streamclonerequested = streamclonerequested
1575 self.streamclonerequested = streamclonerequested
1575 # transaction manager
1576 # transaction manager
1576 self.trmanager = None
1577 self.trmanager = None
1577 # set of common changeset between local and remote before pull
1578 # set of common changeset between local and remote before pull
1578 self.common = None
1579 self.common = None
1579 # set of pulled head
1580 # set of pulled head
1580 self.rheads = None
1581 self.rheads = None
1581 # list of missing changeset to fetch remotely
1582 # list of missing changeset to fetch remotely
1582 self.fetch = None
1583 self.fetch = None
1583 # remote bookmarks data
1584 # remote bookmarks data
1584 self.remotebookmarks = remotebookmarks
1585 self.remotebookmarks = remotebookmarks
1585 # result of changegroup pulling (used as return code by pull)
1586 # result of changegroup pulling (used as return code by pull)
1586 self.cgresult = None
1587 self.cgresult = None
1587 # list of step already done
1588 # list of step already done
1588 self.stepsdone = set()
1589 self.stepsdone = set()
1589 # Whether we attempted a clone from pre-generated bundles.
1590 # Whether we attempted a clone from pre-generated bundles.
1590 self.clonebundleattempted = False
1591 self.clonebundleattempted = False
1591 # Set of file patterns to include.
1592 # Set of file patterns to include.
1592 self.includepats = includepats
1593 self.includepats = includepats
1593 # Set of file patterns to exclude.
1594 # Set of file patterns to exclude.
1594 self.excludepats = excludepats
1595 self.excludepats = excludepats
1595 # Number of ancestor changesets to pull from each pulled head.
1596 # Number of ancestor changesets to pull from each pulled head.
1596 self.depth = depth
1597 self.depth = depth
1597
1598
1598 @util.propertycache
1599 @util.propertycache
1599 def pulledsubset(self):
1600 def pulledsubset(self):
1600 """heads of the set of changeset target by the pull"""
1601 """heads of the set of changeset target by the pull"""
1601 # compute target subset
1602 # compute target subset
1602 if self.heads is None:
1603 if self.heads is None:
1603 # We pulled every thing possible
1604 # We pulled every thing possible
1604 # sync on everything common
1605 # sync on everything common
1605 c = set(self.common)
1606 c = set(self.common)
1606 ret = list(self.common)
1607 ret = list(self.common)
1607 for n in self.rheads:
1608 for n in self.rheads:
1608 if n not in c:
1609 if n not in c:
1609 ret.append(n)
1610 ret.append(n)
1610 return ret
1611 return ret
1611 else:
1612 else:
1612 # We pulled a specific subset
1613 # We pulled a specific subset
1613 # sync on this subset
1614 # sync on this subset
1614 return self.heads
1615 return self.heads
1615
1616
1616 @util.propertycache
1617 @util.propertycache
1617 def canusebundle2(self):
1618 def canusebundle2(self):
1618 return not _forcebundle1(self)
1619 return not _forcebundle1(self)
1619
1620
1620 @util.propertycache
1621 @util.propertycache
1621 def remotebundle2caps(self):
1622 def remotebundle2caps(self):
1622 return bundle2.bundle2caps(self.remote)
1623 return bundle2.bundle2caps(self.remote)
1623
1624
1624 def gettransaction(self):
1625 def gettransaction(self):
1625 # deprecated; talk to trmanager directly
1626 # deprecated; talk to trmanager directly
1626 return self.trmanager.transaction()
1627 return self.trmanager.transaction()
1627
1628
1628
1629
1629 class transactionmanager(util.transactional):
1630 class transactionmanager(util.transactional):
1630 """An object to manage the life cycle of a transaction
1631 """An object to manage the life cycle of a transaction
1631
1632
1632 It creates the transaction on demand and calls the appropriate hooks when
1633 It creates the transaction on demand and calls the appropriate hooks when
1633 closing the transaction."""
1634 closing the transaction."""
1634
1635
1635 def __init__(self, repo, source, url):
1636 def __init__(self, repo, source, url):
1636 self.repo = repo
1637 self.repo = repo
1637 self.source = source
1638 self.source = source
1638 self.url = url
1639 self.url = url
1639 self._tr = None
1640 self._tr = None
1640
1641
1641 def transaction(self):
1642 def transaction(self):
1642 """Return an open transaction object, constructing if necessary"""
1643 """Return an open transaction object, constructing if necessary"""
1643 if not self._tr:
1644 if not self._tr:
1644 trname = b'%s\n%s' % (self.source, util.hidepassword(self.url))
1645 trname = b'%s\n%s' % (self.source, util.hidepassword(self.url))
1645 self._tr = self.repo.transaction(trname)
1646 self._tr = self.repo.transaction(trname)
1646 self._tr.hookargs[b'source'] = self.source
1647 self._tr.hookargs[b'source'] = self.source
1647 self._tr.hookargs[b'url'] = self.url
1648 self._tr.hookargs[b'url'] = self.url
1648 return self._tr
1649 return self._tr
1649
1650
1650 def close(self):
1651 def close(self):
1651 """close transaction if created"""
1652 """close transaction if created"""
1652 if self._tr is not None:
1653 if self._tr is not None:
1653 self._tr.close()
1654 self._tr.close()
1654
1655
1655 def release(self):
1656 def release(self):
1656 """release transaction if created"""
1657 """release transaction if created"""
1657 if self._tr is not None:
1658 if self._tr is not None:
1658 self._tr.release()
1659 self._tr.release()
1659
1660
1660
1661
1661 def listkeys(remote, namespace):
1662 def listkeys(remote, namespace):
1662 with remote.commandexecutor() as e:
1663 with remote.commandexecutor() as e:
1663 return e.callcommand(b'listkeys', {b'namespace': namespace}).result()
1664 return e.callcommand(b'listkeys', {b'namespace': namespace}).result()
1664
1665
1665
1666
1666 def _fullpullbundle2(repo, pullop):
1667 def _fullpullbundle2(repo, pullop):
1667 # The server may send a partial reply, i.e. when inlining
1668 # The server may send a partial reply, i.e. when inlining
1668 # pre-computed bundles. In that case, update the common
1669 # pre-computed bundles. In that case, update the common
1669 # set based on the results and pull another bundle.
1670 # set based on the results and pull another bundle.
1670 #
1671 #
1671 # There are two indicators that the process is finished:
1672 # There are two indicators that the process is finished:
1672 # - no changeset has been added, or
1673 # - no changeset has been added, or
1673 # - all remote heads are known locally.
1674 # - all remote heads are known locally.
1674 # The head check must use the unfiltered view as obsoletion
1675 # The head check must use the unfiltered view as obsoletion
1675 # markers can hide heads.
1676 # markers can hide heads.
1676 unfi = repo.unfiltered()
1677 unfi = repo.unfiltered()
1677 unficl = unfi.changelog
1678 unficl = unfi.changelog
1678
1679
1679 def headsofdiff(h1, h2):
1680 def headsofdiff(h1, h2):
1680 """Returns heads(h1 % h2)"""
1681 """Returns heads(h1 % h2)"""
1681 res = unfi.set(b'heads(%ln %% %ln)', h1, h2)
1682 res = unfi.set(b'heads(%ln %% %ln)', h1, h2)
1682 return {ctx.node() for ctx in res}
1683 return {ctx.node() for ctx in res}
1683
1684
1684 def headsofunion(h1, h2):
1685 def headsofunion(h1, h2):
1685 """Returns heads((h1 + h2) - null)"""
1686 """Returns heads((h1 + h2) - null)"""
1686 res = unfi.set(b'heads((%ln + %ln - null))', h1, h2)
1687 res = unfi.set(b'heads((%ln + %ln - null))', h1, h2)
1687 return {ctx.node() for ctx in res}
1688 return {ctx.node() for ctx in res}
1688
1689
1689 while True:
1690 while True:
1690 old_heads = unficl.heads()
1691 old_heads = unficl.heads()
1691 clstart = len(unficl)
1692 clstart = len(unficl)
1692 _pullbundle2(pullop)
1693 _pullbundle2(pullop)
1693 if repository.NARROW_REQUIREMENT in repo.requirements:
1694 if repository.NARROW_REQUIREMENT in repo.requirements:
1694 # XXX narrow clones filter the heads on the server side during
1695 # XXX narrow clones filter the heads on the server side during
1695 # XXX getbundle and result in partial replies as well.
1696 # XXX getbundle and result in partial replies as well.
1696 # XXX Disable pull bundles in this case as band aid to avoid
1697 # XXX Disable pull bundles in this case as band aid to avoid
1697 # XXX extra round trips.
1698 # XXX extra round trips.
1698 break
1699 break
1699 if clstart == len(unficl):
1700 if clstart == len(unficl):
1700 break
1701 break
1701 if all(unficl.hasnode(n) for n in pullop.rheads):
1702 if all(unficl.hasnode(n) for n in pullop.rheads):
1702 break
1703 break
1703 new_heads = headsofdiff(unficl.heads(), old_heads)
1704 new_heads = headsofdiff(unficl.heads(), old_heads)
1704 pullop.common = headsofunion(new_heads, pullop.common)
1705 pullop.common = headsofunion(new_heads, pullop.common)
1705 pullop.rheads = set(pullop.rheads) - pullop.common
1706 pullop.rheads = set(pullop.rheads) - pullop.common
1706
1707
1707
1708
1709 def add_confirm_callback(repo, pullop):
1710 """ adds a finalize callback to transaction which can be used to show stats
1711 to user and confirm the pull before committing transaction """
1712
1713 tr = pullop.trmanager.transaction()
1714 scmutil.registersummarycallback(
1715 repo, tr, txnname=b'pull', as_validator=True
1716 )
1717 reporef = weakref.ref(repo.unfiltered())
1718
1719 def prompt(tr):
1720 repo = reporef()
1721 cm = _(b'accept incoming changes (yn)?$$ &Yes $$ &No')
1722 if repo.ui.promptchoice(cm):
1723 raise error.Abort("user aborted")
1724
1725 tr.addvalidator(b'900-pull-prompt', prompt)
1726
1727
1708 def pull(
1728 def pull(
1709 repo,
1729 repo,
1710 remote,
1730 remote,
1711 heads=None,
1731 heads=None,
1712 force=False,
1732 force=False,
1713 bookmarks=(),
1733 bookmarks=(),
1714 opargs=None,
1734 opargs=None,
1715 streamclonerequested=None,
1735 streamclonerequested=None,
1716 includepats=None,
1736 includepats=None,
1717 excludepats=None,
1737 excludepats=None,
1718 depth=None,
1738 depth=None,
1739 confirm=None,
1719 ):
1740 ):
1720 """Fetch repository data from a remote.
1741 """Fetch repository data from a remote.
1721
1742
1722 This is the main function used to retrieve data from a remote repository.
1743 This is the main function used to retrieve data from a remote repository.
1723
1744
1724 ``repo`` is the local repository to clone into.
1745 ``repo`` is the local repository to clone into.
1725 ``remote`` is a peer instance.
1746 ``remote`` is a peer instance.
1726 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1747 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1727 default) means to pull everything from the remote.
1748 default) means to pull everything from the remote.
1728 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1749 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1729 default, all remote bookmarks are pulled.
1750 default, all remote bookmarks are pulled.
1730 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1751 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1731 initialization.
1752 initialization.
1732 ``streamclonerequested`` is a boolean indicating whether a "streaming
1753 ``streamclonerequested`` is a boolean indicating whether a "streaming
1733 clone" is requested. A "streaming clone" is essentially a raw file copy
1754 clone" is requested. A "streaming clone" is essentially a raw file copy
1734 of revlogs from the server. This only works when the local repository is
1755 of revlogs from the server. This only works when the local repository is
1735 empty. The default value of ``None`` means to respect the server
1756 empty. The default value of ``None`` means to respect the server
1736 configuration for preferring stream clones.
1757 configuration for preferring stream clones.
1737 ``includepats`` and ``excludepats`` define explicit file patterns to
1758 ``includepats`` and ``excludepats`` define explicit file patterns to
1738 include and exclude in storage, respectively. If not defined, narrow
1759 include and exclude in storage, respectively. If not defined, narrow
1739 patterns from the repo instance are used, if available.
1760 patterns from the repo instance are used, if available.
1740 ``depth`` is an integer indicating the DAG depth of history we're
1761 ``depth`` is an integer indicating the DAG depth of history we're
1741 interested in. If defined, for each revision specified in ``heads``, we
1762 interested in. If defined, for each revision specified in ``heads``, we
1742 will fetch up to this many of its ancestors and data associated with them.
1763 will fetch up to this many of its ancestors and data associated with them.
1764 ``confirm`` is a boolean indicating whether the pull should be confirmed
1765 before committing the transaction. This overrides HGPLAIN.
1743
1766
1744 Returns the ``pulloperation`` created for this pull.
1767 Returns the ``pulloperation`` created for this pull.
1745 """
1768 """
1746 if opargs is None:
1769 if opargs is None:
1747 opargs = {}
1770 opargs = {}
1748
1771
1749 # We allow the narrow patterns to be passed in explicitly to provide more
1772 # We allow the narrow patterns to be passed in explicitly to provide more
1750 # flexibility for API consumers.
1773 # flexibility for API consumers.
1751 if includepats or excludepats:
1774 if includepats or excludepats:
1752 includepats = includepats or set()
1775 includepats = includepats or set()
1753 excludepats = excludepats or set()
1776 excludepats = excludepats or set()
1754 else:
1777 else:
1755 includepats, excludepats = repo.narrowpats
1778 includepats, excludepats = repo.narrowpats
1756
1779
1757 narrowspec.validatepatterns(includepats)
1780 narrowspec.validatepatterns(includepats)
1758 narrowspec.validatepatterns(excludepats)
1781 narrowspec.validatepatterns(excludepats)
1759
1782
1760 pullop = pulloperation(
1783 pullop = pulloperation(
1761 repo,
1784 repo,
1762 remote,
1785 remote,
1763 heads,
1786 heads,
1764 force,
1787 force,
1765 bookmarks=bookmarks,
1788 bookmarks=bookmarks,
1766 streamclonerequested=streamclonerequested,
1789 streamclonerequested=streamclonerequested,
1767 includepats=includepats,
1790 includepats=includepats,
1768 excludepats=excludepats,
1791 excludepats=excludepats,
1769 depth=depth,
1792 depth=depth,
1770 **pycompat.strkwargs(opargs)
1793 **pycompat.strkwargs(opargs)
1771 )
1794 )
1772
1795
1773 peerlocal = pullop.remote.local()
1796 peerlocal = pullop.remote.local()
1774 if peerlocal:
1797 if peerlocal:
1775 missing = set(peerlocal.requirements) - pullop.repo.supported
1798 missing = set(peerlocal.requirements) - pullop.repo.supported
1776 if missing:
1799 if missing:
1777 msg = _(
1800 msg = _(
1778 b"required features are not"
1801 b"required features are not"
1779 b" supported in the destination:"
1802 b" supported in the destination:"
1780 b" %s"
1803 b" %s"
1781 ) % (b', '.join(sorted(missing)))
1804 ) % (b', '.join(sorted(missing)))
1782 raise error.Abort(msg)
1805 raise error.Abort(msg)
1783
1806
1784 pullop.trmanager = transactionmanager(repo, b'pull', remote.url())
1807 pullop.trmanager = transactionmanager(repo, b'pull', remote.url())
1785 wlock = util.nullcontextmanager()
1808 wlock = util.nullcontextmanager()
1786 if not bookmod.bookmarksinstore(repo):
1809 if not bookmod.bookmarksinstore(repo):
1787 wlock = repo.wlock()
1810 wlock = repo.wlock()
1788 with wlock, repo.lock(), pullop.trmanager:
1811 with wlock, repo.lock(), pullop.trmanager:
1812 if confirm or (
1813 repo.ui.configbool(b"pull", b"confirm") and not repo.ui.plain()
1814 ):
1815 add_confirm_callback(repo, pullop)
1816
1789 # Use the modern wire protocol, if available.
1817 # Use the modern wire protocol, if available.
1790 if remote.capable(b'command-changesetdata'):
1818 if remote.capable(b'command-changesetdata'):
1791 exchangev2.pull(pullop)
1819 exchangev2.pull(pullop)
1792 else:
1820 else:
1793 # This should ideally be in _pullbundle2(). However, it needs to run
1821 # This should ideally be in _pullbundle2(). However, it needs to run
1794 # before discovery to avoid extra work.
1822 # before discovery to avoid extra work.
1795 _maybeapplyclonebundle(pullop)
1823 _maybeapplyclonebundle(pullop)
1796 streamclone.maybeperformlegacystreamclone(pullop)
1824 streamclone.maybeperformlegacystreamclone(pullop)
1797 _pulldiscovery(pullop)
1825 _pulldiscovery(pullop)
1798 if pullop.canusebundle2:
1826 if pullop.canusebundle2:
1799 _fullpullbundle2(repo, pullop)
1827 _fullpullbundle2(repo, pullop)
1800 _pullchangeset(pullop)
1828 _pullchangeset(pullop)
1801 _pullphase(pullop)
1829 _pullphase(pullop)
1802 _pullbookmarks(pullop)
1830 _pullbookmarks(pullop)
1803 _pullobsolete(pullop)
1831 _pullobsolete(pullop)
1804
1832
1805 # storing remotenames
1833 # storing remotenames
1806 if repo.ui.configbool(b'experimental', b'remotenames'):
1834 if repo.ui.configbool(b'experimental', b'remotenames'):
1807 logexchange.pullremotenames(repo, remote)
1835 logexchange.pullremotenames(repo, remote)
1808
1836
1809 return pullop
1837 return pullop
1810
1838
1811
1839
1812 # list of steps to perform discovery before pull
1840 # list of steps to perform discovery before pull
1813 pulldiscoveryorder = []
1841 pulldiscoveryorder = []
1814
1842
1815 # Mapping between step name and function
1843 # Mapping between step name and function
1816 #
1844 #
1817 # This exists to help extensions wrap steps if necessary
1845 # This exists to help extensions wrap steps if necessary
1818 pulldiscoverymapping = {}
1846 pulldiscoverymapping = {}
1819
1847
1820
1848
1821 def pulldiscovery(stepname):
1849 def pulldiscovery(stepname):
1822 """decorator for function performing discovery before pull
1850 """decorator for function performing discovery before pull
1823
1851
1824 The function is added to the step -> function mapping and appended to the
1852 The function is added to the step -> function mapping and appended to the
1825 list of steps. Beware that decorated function will be added in order (this
1853 list of steps. Beware that decorated function will be added in order (this
1826 may matter).
1854 may matter).
1827
1855
1828 You can only use this decorator for a new step, if you want to wrap a step
1856 You can only use this decorator for a new step, if you want to wrap a step
1829 from an extension, change the pulldiscovery dictionary directly."""
1857 from an extension, change the pulldiscovery dictionary directly."""
1830
1858
1831 def dec(func):
1859 def dec(func):
1832 assert stepname not in pulldiscoverymapping
1860 assert stepname not in pulldiscoverymapping
1833 pulldiscoverymapping[stepname] = func
1861 pulldiscoverymapping[stepname] = func
1834 pulldiscoveryorder.append(stepname)
1862 pulldiscoveryorder.append(stepname)
1835 return func
1863 return func
1836
1864
1837 return dec
1865 return dec
1838
1866
1839
1867
1840 def _pulldiscovery(pullop):
1868 def _pulldiscovery(pullop):
1841 """Run all discovery steps"""
1869 """Run all discovery steps"""
1842 for stepname in pulldiscoveryorder:
1870 for stepname in pulldiscoveryorder:
1843 step = pulldiscoverymapping[stepname]
1871 step = pulldiscoverymapping[stepname]
1844 step(pullop)
1872 step(pullop)
1845
1873
1846
1874
1847 @pulldiscovery(b'b1:bookmarks')
1875 @pulldiscovery(b'b1:bookmarks')
1848 def _pullbookmarkbundle1(pullop):
1876 def _pullbookmarkbundle1(pullop):
1849 """fetch bookmark data in bundle1 case
1877 """fetch bookmark data in bundle1 case
1850
1878
1851 If not using bundle2, we have to fetch bookmarks before changeset
1879 If not using bundle2, we have to fetch bookmarks before changeset
1852 discovery to reduce the chance and impact of race conditions."""
1880 discovery to reduce the chance and impact of race conditions."""
1853 if pullop.remotebookmarks is not None:
1881 if pullop.remotebookmarks is not None:
1854 return
1882 return
1855 if pullop.canusebundle2 and b'listkeys' in pullop.remotebundle2caps:
1883 if pullop.canusebundle2 and b'listkeys' in pullop.remotebundle2caps:
1856 # all known bundle2 servers now support listkeys, but lets be nice with
1884 # all known bundle2 servers now support listkeys, but lets be nice with
1857 # new implementation.
1885 # new implementation.
1858 return
1886 return
1859 books = listkeys(pullop.remote, b'bookmarks')
1887 books = listkeys(pullop.remote, b'bookmarks')
1860 pullop.remotebookmarks = bookmod.unhexlifybookmarks(books)
1888 pullop.remotebookmarks = bookmod.unhexlifybookmarks(books)
1861
1889
1862
1890
1863 @pulldiscovery(b'changegroup')
1891 @pulldiscovery(b'changegroup')
1864 def _pulldiscoverychangegroup(pullop):
1892 def _pulldiscoverychangegroup(pullop):
1865 """discovery phase for the pull
1893 """discovery phase for the pull
1866
1894
1867 Current handle changeset discovery only, will change handle all discovery
1895 Current handle changeset discovery only, will change handle all discovery
1868 at some point."""
1896 at some point."""
1869 tmp = discovery.findcommonincoming(
1897 tmp = discovery.findcommonincoming(
1870 pullop.repo, pullop.remote, heads=pullop.heads, force=pullop.force
1898 pullop.repo, pullop.remote, heads=pullop.heads, force=pullop.force
1871 )
1899 )
1872 common, fetch, rheads = tmp
1900 common, fetch, rheads = tmp
1873 has_node = pullop.repo.unfiltered().changelog.index.has_node
1901 has_node = pullop.repo.unfiltered().changelog.index.has_node
1874 if fetch and rheads:
1902 if fetch and rheads:
1875 # If a remote heads is filtered locally, put in back in common.
1903 # If a remote heads is filtered locally, put in back in common.
1876 #
1904 #
1877 # This is a hackish solution to catch most of "common but locally
1905 # This is a hackish solution to catch most of "common but locally
1878 # hidden situation". We do not performs discovery on unfiltered
1906 # hidden situation". We do not performs discovery on unfiltered
1879 # repository because it end up doing a pathological amount of round
1907 # repository because it end up doing a pathological amount of round
1880 # trip for w huge amount of changeset we do not care about.
1908 # trip for w huge amount of changeset we do not care about.
1881 #
1909 #
1882 # If a set of such "common but filtered" changeset exist on the server
1910 # If a set of such "common but filtered" changeset exist on the server
1883 # but are not including a remote heads, we'll not be able to detect it,
1911 # but are not including a remote heads, we'll not be able to detect it,
1884 scommon = set(common)
1912 scommon = set(common)
1885 for n in rheads:
1913 for n in rheads:
1886 if has_node(n):
1914 if has_node(n):
1887 if n not in scommon:
1915 if n not in scommon:
1888 common.append(n)
1916 common.append(n)
1889 if set(rheads).issubset(set(common)):
1917 if set(rheads).issubset(set(common)):
1890 fetch = []
1918 fetch = []
1891 pullop.common = common
1919 pullop.common = common
1892 pullop.fetch = fetch
1920 pullop.fetch = fetch
1893 pullop.rheads = rheads
1921 pullop.rheads = rheads
1894
1922
1895
1923
1896 def _pullbundle2(pullop):
1924 def _pullbundle2(pullop):
1897 """pull data using bundle2
1925 """pull data using bundle2
1898
1926
1899 For now, the only supported data are changegroup."""
1927 For now, the only supported data are changegroup."""
1900 kwargs = {b'bundlecaps': caps20to10(pullop.repo, role=b'client')}
1928 kwargs = {b'bundlecaps': caps20to10(pullop.repo, role=b'client')}
1901
1929
1902 # make ui easier to access
1930 # make ui easier to access
1903 ui = pullop.repo.ui
1931 ui = pullop.repo.ui
1904
1932
1905 # At the moment we don't do stream clones over bundle2. If that is
1933 # At the moment we don't do stream clones over bundle2. If that is
1906 # implemented then here's where the check for that will go.
1934 # implemented then here's where the check for that will go.
1907 streaming = streamclone.canperformstreamclone(pullop, bundle2=True)[0]
1935 streaming = streamclone.canperformstreamclone(pullop, bundle2=True)[0]
1908
1936
1909 # declare pull perimeters
1937 # declare pull perimeters
1910 kwargs[b'common'] = pullop.common
1938 kwargs[b'common'] = pullop.common
1911 kwargs[b'heads'] = pullop.heads or pullop.rheads
1939 kwargs[b'heads'] = pullop.heads or pullop.rheads
1912
1940
1913 # check server supports narrow and then adding includepats and excludepats
1941 # check server supports narrow and then adding includepats and excludepats
1914 servernarrow = pullop.remote.capable(wireprototypes.NARROWCAP)
1942 servernarrow = pullop.remote.capable(wireprototypes.NARROWCAP)
1915 if servernarrow and pullop.includepats:
1943 if servernarrow and pullop.includepats:
1916 kwargs[b'includepats'] = pullop.includepats
1944 kwargs[b'includepats'] = pullop.includepats
1917 if servernarrow and pullop.excludepats:
1945 if servernarrow and pullop.excludepats:
1918 kwargs[b'excludepats'] = pullop.excludepats
1946 kwargs[b'excludepats'] = pullop.excludepats
1919
1947
1920 if streaming:
1948 if streaming:
1921 kwargs[b'cg'] = False
1949 kwargs[b'cg'] = False
1922 kwargs[b'stream'] = True
1950 kwargs[b'stream'] = True
1923 pullop.stepsdone.add(b'changegroup')
1951 pullop.stepsdone.add(b'changegroup')
1924 pullop.stepsdone.add(b'phases')
1952 pullop.stepsdone.add(b'phases')
1925
1953
1926 else:
1954 else:
1927 # pulling changegroup
1955 # pulling changegroup
1928 pullop.stepsdone.add(b'changegroup')
1956 pullop.stepsdone.add(b'changegroup')
1929
1957
1930 kwargs[b'cg'] = pullop.fetch
1958 kwargs[b'cg'] = pullop.fetch
1931
1959
1932 legacyphase = b'phases' in ui.configlist(b'devel', b'legacy.exchange')
1960 legacyphase = b'phases' in ui.configlist(b'devel', b'legacy.exchange')
1933 hasbinaryphase = b'heads' in pullop.remotebundle2caps.get(b'phases', ())
1961 hasbinaryphase = b'heads' in pullop.remotebundle2caps.get(b'phases', ())
1934 if not legacyphase and hasbinaryphase:
1962 if not legacyphase and hasbinaryphase:
1935 kwargs[b'phases'] = True
1963 kwargs[b'phases'] = True
1936 pullop.stepsdone.add(b'phases')
1964 pullop.stepsdone.add(b'phases')
1937
1965
1938 if b'listkeys' in pullop.remotebundle2caps:
1966 if b'listkeys' in pullop.remotebundle2caps:
1939 if b'phases' not in pullop.stepsdone:
1967 if b'phases' not in pullop.stepsdone:
1940 kwargs[b'listkeys'] = [b'phases']
1968 kwargs[b'listkeys'] = [b'phases']
1941
1969
1942 bookmarksrequested = False
1970 bookmarksrequested = False
1943 legacybookmark = b'bookmarks' in ui.configlist(b'devel', b'legacy.exchange')
1971 legacybookmark = b'bookmarks' in ui.configlist(b'devel', b'legacy.exchange')
1944 hasbinarybook = b'bookmarks' in pullop.remotebundle2caps
1972 hasbinarybook = b'bookmarks' in pullop.remotebundle2caps
1945
1973
1946 if pullop.remotebookmarks is not None:
1974 if pullop.remotebookmarks is not None:
1947 pullop.stepsdone.add(b'request-bookmarks')
1975 pullop.stepsdone.add(b'request-bookmarks')
1948
1976
1949 if (
1977 if (
1950 b'request-bookmarks' not in pullop.stepsdone
1978 b'request-bookmarks' not in pullop.stepsdone
1951 and pullop.remotebookmarks is None
1979 and pullop.remotebookmarks is None
1952 and not legacybookmark
1980 and not legacybookmark
1953 and hasbinarybook
1981 and hasbinarybook
1954 ):
1982 ):
1955 kwargs[b'bookmarks'] = True
1983 kwargs[b'bookmarks'] = True
1956 bookmarksrequested = True
1984 bookmarksrequested = True
1957
1985
1958 if b'listkeys' in pullop.remotebundle2caps:
1986 if b'listkeys' in pullop.remotebundle2caps:
1959 if b'request-bookmarks' not in pullop.stepsdone:
1987 if b'request-bookmarks' not in pullop.stepsdone:
1960 # make sure to always includes bookmark data when migrating
1988 # make sure to always includes bookmark data when migrating
1961 # `hg incoming --bundle` to using this function.
1989 # `hg incoming --bundle` to using this function.
1962 pullop.stepsdone.add(b'request-bookmarks')
1990 pullop.stepsdone.add(b'request-bookmarks')
1963 kwargs.setdefault(b'listkeys', []).append(b'bookmarks')
1991 kwargs.setdefault(b'listkeys', []).append(b'bookmarks')
1964
1992
1965 # If this is a full pull / clone and the server supports the clone bundles
1993 # If this is a full pull / clone and the server supports the clone bundles
1966 # feature, tell the server whether we attempted a clone bundle. The
1994 # feature, tell the server whether we attempted a clone bundle. The
1967 # presence of this flag indicates the client supports clone bundles. This
1995 # presence of this flag indicates the client supports clone bundles. This
1968 # will enable the server to treat clients that support clone bundles
1996 # will enable the server to treat clients that support clone bundles
1969 # differently from those that don't.
1997 # differently from those that don't.
1970 if (
1998 if (
1971 pullop.remote.capable(b'clonebundles')
1999 pullop.remote.capable(b'clonebundles')
1972 and pullop.heads is None
2000 and pullop.heads is None
1973 and list(pullop.common) == [nullid]
2001 and list(pullop.common) == [nullid]
1974 ):
2002 ):
1975 kwargs[b'cbattempted'] = pullop.clonebundleattempted
2003 kwargs[b'cbattempted'] = pullop.clonebundleattempted
1976
2004
1977 if streaming:
2005 if streaming:
1978 pullop.repo.ui.status(_(b'streaming all changes\n'))
2006 pullop.repo.ui.status(_(b'streaming all changes\n'))
1979 elif not pullop.fetch:
2007 elif not pullop.fetch:
1980 pullop.repo.ui.status(_(b"no changes found\n"))
2008 pullop.repo.ui.status(_(b"no changes found\n"))
1981 pullop.cgresult = 0
2009 pullop.cgresult = 0
1982 else:
2010 else:
1983 if pullop.heads is None and list(pullop.common) == [nullid]:
2011 if pullop.heads is None and list(pullop.common) == [nullid]:
1984 pullop.repo.ui.status(_(b"requesting all changes\n"))
2012 pullop.repo.ui.status(_(b"requesting all changes\n"))
1985 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
2013 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1986 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
2014 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
1987 if obsolete.commonversion(remoteversions) is not None:
2015 if obsolete.commonversion(remoteversions) is not None:
1988 kwargs[b'obsmarkers'] = True
2016 kwargs[b'obsmarkers'] = True
1989 pullop.stepsdone.add(b'obsmarkers')
2017 pullop.stepsdone.add(b'obsmarkers')
1990 _pullbundle2extraprepare(pullop, kwargs)
2018 _pullbundle2extraprepare(pullop, kwargs)
1991
2019
1992 with pullop.remote.commandexecutor() as e:
2020 with pullop.remote.commandexecutor() as e:
1993 args = dict(kwargs)
2021 args = dict(kwargs)
1994 args[b'source'] = b'pull'
2022 args[b'source'] = b'pull'
1995 bundle = e.callcommand(b'getbundle', args).result()
2023 bundle = e.callcommand(b'getbundle', args).result()
1996
2024
1997 try:
2025 try:
1998 op = bundle2.bundleoperation(
2026 op = bundle2.bundleoperation(
1999 pullop.repo, pullop.gettransaction, source=b'pull'
2027 pullop.repo, pullop.gettransaction, source=b'pull'
2000 )
2028 )
2001 op.modes[b'bookmarks'] = b'records'
2029 op.modes[b'bookmarks'] = b'records'
2002 bundle2.processbundle(pullop.repo, bundle, op=op)
2030 bundle2.processbundle(pullop.repo, bundle, op=op)
2003 except bundle2.AbortFromPart as exc:
2031 except bundle2.AbortFromPart as exc:
2004 pullop.repo.ui.status(_(b'remote: abort: %s\n') % exc)
2032 pullop.repo.ui.status(_(b'remote: abort: %s\n') % exc)
2005 raise error.Abort(_(b'pull failed on remote'), hint=exc.hint)
2033 raise error.Abort(_(b'pull failed on remote'), hint=exc.hint)
2006 except error.BundleValueError as exc:
2034 except error.BundleValueError as exc:
2007 raise error.Abort(_(b'missing support for %s') % exc)
2035 raise error.Abort(_(b'missing support for %s') % exc)
2008
2036
2009 if pullop.fetch:
2037 if pullop.fetch:
2010 pullop.cgresult = bundle2.combinechangegroupresults(op)
2038 pullop.cgresult = bundle2.combinechangegroupresults(op)
2011
2039
2012 # processing phases change
2040 # processing phases change
2013 for namespace, value in op.records[b'listkeys']:
2041 for namespace, value in op.records[b'listkeys']:
2014 if namespace == b'phases':
2042 if namespace == b'phases':
2015 _pullapplyphases(pullop, value)
2043 _pullapplyphases(pullop, value)
2016
2044
2017 # processing bookmark update
2045 # processing bookmark update
2018 if bookmarksrequested:
2046 if bookmarksrequested:
2019 books = {}
2047 books = {}
2020 for record in op.records[b'bookmarks']:
2048 for record in op.records[b'bookmarks']:
2021 books[record[b'bookmark']] = record[b"node"]
2049 books[record[b'bookmark']] = record[b"node"]
2022 pullop.remotebookmarks = books
2050 pullop.remotebookmarks = books
2023 else:
2051 else:
2024 for namespace, value in op.records[b'listkeys']:
2052 for namespace, value in op.records[b'listkeys']:
2025 if namespace == b'bookmarks':
2053 if namespace == b'bookmarks':
2026 pullop.remotebookmarks = bookmod.unhexlifybookmarks(value)
2054 pullop.remotebookmarks = bookmod.unhexlifybookmarks(value)
2027
2055
2028 # bookmark data were either already there or pulled in the bundle
2056 # bookmark data were either already there or pulled in the bundle
2029 if pullop.remotebookmarks is not None:
2057 if pullop.remotebookmarks is not None:
2030 _pullbookmarks(pullop)
2058 _pullbookmarks(pullop)
2031
2059
2032
2060
2033 def _pullbundle2extraprepare(pullop, kwargs):
2061 def _pullbundle2extraprepare(pullop, kwargs):
2034 """hook function so that extensions can extend the getbundle call"""
2062 """hook function so that extensions can extend the getbundle call"""
2035
2063
2036
2064
2037 def _pullchangeset(pullop):
2065 def _pullchangeset(pullop):
2038 """pull changeset from unbundle into the local repo"""
2066 """pull changeset from unbundle into the local repo"""
2039 # We delay the open of the transaction as late as possible so we
2067 # We delay the open of the transaction as late as possible so we
2040 # don't open transaction for nothing or you break future useful
2068 # don't open transaction for nothing or you break future useful
2041 # rollback call
2069 # rollback call
2042 if b'changegroup' in pullop.stepsdone:
2070 if b'changegroup' in pullop.stepsdone:
2043 return
2071 return
2044 pullop.stepsdone.add(b'changegroup')
2072 pullop.stepsdone.add(b'changegroup')
2045 if not pullop.fetch:
2073 if not pullop.fetch:
2046 pullop.repo.ui.status(_(b"no changes found\n"))
2074 pullop.repo.ui.status(_(b"no changes found\n"))
2047 pullop.cgresult = 0
2075 pullop.cgresult = 0
2048 return
2076 return
2049 tr = pullop.gettransaction()
2077 tr = pullop.gettransaction()
2050 if pullop.heads is None and list(pullop.common) == [nullid]:
2078 if pullop.heads is None and list(pullop.common) == [nullid]:
2051 pullop.repo.ui.status(_(b"requesting all changes\n"))
2079 pullop.repo.ui.status(_(b"requesting all changes\n"))
2052 elif pullop.heads is None and pullop.remote.capable(b'changegroupsubset'):
2080 elif pullop.heads is None and pullop.remote.capable(b'changegroupsubset'):
2053 # issue1320, avoid a race if remote changed after discovery
2081 # issue1320, avoid a race if remote changed after discovery
2054 pullop.heads = pullop.rheads
2082 pullop.heads = pullop.rheads
2055
2083
2056 if pullop.remote.capable(b'getbundle'):
2084 if pullop.remote.capable(b'getbundle'):
2057 # TODO: get bundlecaps from remote
2085 # TODO: get bundlecaps from remote
2058 cg = pullop.remote.getbundle(
2086 cg = pullop.remote.getbundle(
2059 b'pull', common=pullop.common, heads=pullop.heads or pullop.rheads
2087 b'pull', common=pullop.common, heads=pullop.heads or pullop.rheads
2060 )
2088 )
2061 elif pullop.heads is None:
2089 elif pullop.heads is None:
2062 with pullop.remote.commandexecutor() as e:
2090 with pullop.remote.commandexecutor() as e:
2063 cg = e.callcommand(
2091 cg = e.callcommand(
2064 b'changegroup', {b'nodes': pullop.fetch, b'source': b'pull',}
2092 b'changegroup', {b'nodes': pullop.fetch, b'source': b'pull',}
2065 ).result()
2093 ).result()
2066
2094
2067 elif not pullop.remote.capable(b'changegroupsubset'):
2095 elif not pullop.remote.capable(b'changegroupsubset'):
2068 raise error.Abort(
2096 raise error.Abort(
2069 _(
2097 _(
2070 b"partial pull cannot be done because "
2098 b"partial pull cannot be done because "
2071 b"other repository doesn't support "
2099 b"other repository doesn't support "
2072 b"changegroupsubset."
2100 b"changegroupsubset."
2073 )
2101 )
2074 )
2102 )
2075 else:
2103 else:
2076 with pullop.remote.commandexecutor() as e:
2104 with pullop.remote.commandexecutor() as e:
2077 cg = e.callcommand(
2105 cg = e.callcommand(
2078 b'changegroupsubset',
2106 b'changegroupsubset',
2079 {
2107 {
2080 b'bases': pullop.fetch,
2108 b'bases': pullop.fetch,
2081 b'heads': pullop.heads,
2109 b'heads': pullop.heads,
2082 b'source': b'pull',
2110 b'source': b'pull',
2083 },
2111 },
2084 ).result()
2112 ).result()
2085
2113
2086 bundleop = bundle2.applybundle(
2114 bundleop = bundle2.applybundle(
2087 pullop.repo, cg, tr, b'pull', pullop.remote.url()
2115 pullop.repo, cg, tr, b'pull', pullop.remote.url()
2088 )
2116 )
2089 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
2117 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
2090
2118
2091
2119
2092 def _pullphase(pullop):
2120 def _pullphase(pullop):
2093 # Get remote phases data from remote
2121 # Get remote phases data from remote
2094 if b'phases' in pullop.stepsdone:
2122 if b'phases' in pullop.stepsdone:
2095 return
2123 return
2096 remotephases = listkeys(pullop.remote, b'phases')
2124 remotephases = listkeys(pullop.remote, b'phases')
2097 _pullapplyphases(pullop, remotephases)
2125 _pullapplyphases(pullop, remotephases)
2098
2126
2099
2127
2100 def _pullapplyphases(pullop, remotephases):
2128 def _pullapplyphases(pullop, remotephases):
2101 """apply phase movement from observed remote state"""
2129 """apply phase movement from observed remote state"""
2102 if b'phases' in pullop.stepsdone:
2130 if b'phases' in pullop.stepsdone:
2103 return
2131 return
2104 pullop.stepsdone.add(b'phases')
2132 pullop.stepsdone.add(b'phases')
2105 publishing = bool(remotephases.get(b'publishing', False))
2133 publishing = bool(remotephases.get(b'publishing', False))
2106 if remotephases and not publishing:
2134 if remotephases and not publishing:
2107 # remote is new and non-publishing
2135 # remote is new and non-publishing
2108 pheads, _dr = phases.analyzeremotephases(
2136 pheads, _dr = phases.analyzeremotephases(
2109 pullop.repo, pullop.pulledsubset, remotephases
2137 pullop.repo, pullop.pulledsubset, remotephases
2110 )
2138 )
2111 dheads = pullop.pulledsubset
2139 dheads = pullop.pulledsubset
2112 else:
2140 else:
2113 # Remote is old or publishing all common changesets
2141 # Remote is old or publishing all common changesets
2114 # should be seen as public
2142 # should be seen as public
2115 pheads = pullop.pulledsubset
2143 pheads = pullop.pulledsubset
2116 dheads = []
2144 dheads = []
2117 unfi = pullop.repo.unfiltered()
2145 unfi = pullop.repo.unfiltered()
2118 phase = unfi._phasecache.phase
2146 phase = unfi._phasecache.phase
2119 rev = unfi.changelog.index.get_rev
2147 rev = unfi.changelog.index.get_rev
2120 public = phases.public
2148 public = phases.public
2121 draft = phases.draft
2149 draft = phases.draft
2122
2150
2123 # exclude changesets already public locally and update the others
2151 # exclude changesets already public locally and update the others
2124 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
2152 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
2125 if pheads:
2153 if pheads:
2126 tr = pullop.gettransaction()
2154 tr = pullop.gettransaction()
2127 phases.advanceboundary(pullop.repo, tr, public, pheads)
2155 phases.advanceboundary(pullop.repo, tr, public, pheads)
2128
2156
2129 # exclude changesets already draft locally and update the others
2157 # exclude changesets already draft locally and update the others
2130 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
2158 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
2131 if dheads:
2159 if dheads:
2132 tr = pullop.gettransaction()
2160 tr = pullop.gettransaction()
2133 phases.advanceboundary(pullop.repo, tr, draft, dheads)
2161 phases.advanceboundary(pullop.repo, tr, draft, dheads)
2134
2162
2135
2163
2136 def _pullbookmarks(pullop):
2164 def _pullbookmarks(pullop):
2137 """process the remote bookmark information to update the local one"""
2165 """process the remote bookmark information to update the local one"""
2138 if b'bookmarks' in pullop.stepsdone:
2166 if b'bookmarks' in pullop.stepsdone:
2139 return
2167 return
2140 pullop.stepsdone.add(b'bookmarks')
2168 pullop.stepsdone.add(b'bookmarks')
2141 repo = pullop.repo
2169 repo = pullop.repo
2142 remotebookmarks = pullop.remotebookmarks
2170 remotebookmarks = pullop.remotebookmarks
2143 bookmod.updatefromremote(
2171 bookmod.updatefromremote(
2144 repo.ui,
2172 repo.ui,
2145 repo,
2173 repo,
2146 remotebookmarks,
2174 remotebookmarks,
2147 pullop.remote.url(),
2175 pullop.remote.url(),
2148 pullop.gettransaction,
2176 pullop.gettransaction,
2149 explicit=pullop.explicitbookmarks,
2177 explicit=pullop.explicitbookmarks,
2150 )
2178 )
2151
2179
2152
2180
2153 def _pullobsolete(pullop):
2181 def _pullobsolete(pullop):
2154 """utility function to pull obsolete markers from a remote
2182 """utility function to pull obsolete markers from a remote
2155
2183
2156 The `gettransaction` is function that return the pull transaction, creating
2184 The `gettransaction` is function that return the pull transaction, creating
2157 one if necessary. We return the transaction to inform the calling code that
2185 one if necessary. We return the transaction to inform the calling code that
2158 a new transaction have been created (when applicable).
2186 a new transaction have been created (when applicable).
2159
2187
2160 Exists mostly to allow overriding for experimentation purpose"""
2188 Exists mostly to allow overriding for experimentation purpose"""
2161 if b'obsmarkers' in pullop.stepsdone:
2189 if b'obsmarkers' in pullop.stepsdone:
2162 return
2190 return
2163 pullop.stepsdone.add(b'obsmarkers')
2191 pullop.stepsdone.add(b'obsmarkers')
2164 tr = None
2192 tr = None
2165 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
2193 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
2166 pullop.repo.ui.debug(b'fetching remote obsolete markers\n')
2194 pullop.repo.ui.debug(b'fetching remote obsolete markers\n')
2167 remoteobs = listkeys(pullop.remote, b'obsolete')
2195 remoteobs = listkeys(pullop.remote, b'obsolete')
2168 if b'dump0' in remoteobs:
2196 if b'dump0' in remoteobs:
2169 tr = pullop.gettransaction()
2197 tr = pullop.gettransaction()
2170 markers = []
2198 markers = []
2171 for key in sorted(remoteobs, reverse=True):
2199 for key in sorted(remoteobs, reverse=True):
2172 if key.startswith(b'dump'):
2200 if key.startswith(b'dump'):
2173 data = util.b85decode(remoteobs[key])
2201 data = util.b85decode(remoteobs[key])
2174 version, newmarks = obsolete._readmarkers(data)
2202 version, newmarks = obsolete._readmarkers(data)
2175 markers += newmarks
2203 markers += newmarks
2176 if markers:
2204 if markers:
2177 pullop.repo.obsstore.add(tr, markers)
2205 pullop.repo.obsstore.add(tr, markers)
2178 pullop.repo.invalidatevolatilesets()
2206 pullop.repo.invalidatevolatilesets()
2179 return tr
2207 return tr
2180
2208
2181
2209
2182 def applynarrowacl(repo, kwargs):
2210 def applynarrowacl(repo, kwargs):
2183 """Apply narrow fetch access control.
2211 """Apply narrow fetch access control.
2184
2212
2185 This massages the named arguments for getbundle wire protocol commands
2213 This massages the named arguments for getbundle wire protocol commands
2186 so requested data is filtered through access control rules.
2214 so requested data is filtered through access control rules.
2187 """
2215 """
2188 ui = repo.ui
2216 ui = repo.ui
2189 # TODO this assumes existence of HTTP and is a layering violation.
2217 # TODO this assumes existence of HTTP and is a layering violation.
2190 username = ui.shortuser(ui.environ.get(b'REMOTE_USER') or ui.username())
2218 username = ui.shortuser(ui.environ.get(b'REMOTE_USER') or ui.username())
2191 user_includes = ui.configlist(
2219 user_includes = ui.configlist(
2192 _NARROWACL_SECTION,
2220 _NARROWACL_SECTION,
2193 username + b'.includes',
2221 username + b'.includes',
2194 ui.configlist(_NARROWACL_SECTION, b'default.includes'),
2222 ui.configlist(_NARROWACL_SECTION, b'default.includes'),
2195 )
2223 )
2196 user_excludes = ui.configlist(
2224 user_excludes = ui.configlist(
2197 _NARROWACL_SECTION,
2225 _NARROWACL_SECTION,
2198 username + b'.excludes',
2226 username + b'.excludes',
2199 ui.configlist(_NARROWACL_SECTION, b'default.excludes'),
2227 ui.configlist(_NARROWACL_SECTION, b'default.excludes'),
2200 )
2228 )
2201 if not user_includes:
2229 if not user_includes:
2202 raise error.Abort(
2230 raise error.Abort(
2203 _(b"%s configuration for user %s is empty")
2231 _(b"%s configuration for user %s is empty")
2204 % (_NARROWACL_SECTION, username)
2232 % (_NARROWACL_SECTION, username)
2205 )
2233 )
2206
2234
2207 user_includes = [
2235 user_includes = [
2208 b'path:.' if p == b'*' else b'path:' + p for p in user_includes
2236 b'path:.' if p == b'*' else b'path:' + p for p in user_includes
2209 ]
2237 ]
2210 user_excludes = [
2238 user_excludes = [
2211 b'path:.' if p == b'*' else b'path:' + p for p in user_excludes
2239 b'path:.' if p == b'*' else b'path:' + p for p in user_excludes
2212 ]
2240 ]
2213
2241
2214 req_includes = set(kwargs.get('includepats', []))
2242 req_includes = set(kwargs.get('includepats', []))
2215 req_excludes = set(kwargs.get('excludepats', []))
2243 req_excludes = set(kwargs.get('excludepats', []))
2216
2244
2217 req_includes, req_excludes, invalid_includes = narrowspec.restrictpatterns(
2245 req_includes, req_excludes, invalid_includes = narrowspec.restrictpatterns(
2218 req_includes, req_excludes, user_includes, user_excludes
2246 req_includes, req_excludes, user_includes, user_excludes
2219 )
2247 )
2220
2248
2221 if invalid_includes:
2249 if invalid_includes:
2222 raise error.Abort(
2250 raise error.Abort(
2223 _(b"The following includes are not accessible for %s: %s")
2251 _(b"The following includes are not accessible for %s: %s")
2224 % (username, stringutil.pprint(invalid_includes))
2252 % (username, stringutil.pprint(invalid_includes))
2225 )
2253 )
2226
2254
2227 new_args = {}
2255 new_args = {}
2228 new_args.update(kwargs)
2256 new_args.update(kwargs)
2229 new_args['narrow'] = True
2257 new_args['narrow'] = True
2230 new_args['narrow_acl'] = True
2258 new_args['narrow_acl'] = True
2231 new_args['includepats'] = req_includes
2259 new_args['includepats'] = req_includes
2232 if req_excludes:
2260 if req_excludes:
2233 new_args['excludepats'] = req_excludes
2261 new_args['excludepats'] = req_excludes
2234
2262
2235 return new_args
2263 return new_args
2236
2264
2237
2265
2238 def _computeellipsis(repo, common, heads, known, match, depth=None):
2266 def _computeellipsis(repo, common, heads, known, match, depth=None):
2239 """Compute the shape of a narrowed DAG.
2267 """Compute the shape of a narrowed DAG.
2240
2268
2241 Args:
2269 Args:
2242 repo: The repository we're transferring.
2270 repo: The repository we're transferring.
2243 common: The roots of the DAG range we're transferring.
2271 common: The roots of the DAG range we're transferring.
2244 May be just [nullid], which means all ancestors of heads.
2272 May be just [nullid], which means all ancestors of heads.
2245 heads: The heads of the DAG range we're transferring.
2273 heads: The heads of the DAG range we're transferring.
2246 match: The narrowmatcher that allows us to identify relevant changes.
2274 match: The narrowmatcher that allows us to identify relevant changes.
2247 depth: If not None, only consider nodes to be full nodes if they are at
2275 depth: If not None, only consider nodes to be full nodes if they are at
2248 most depth changesets away from one of heads.
2276 most depth changesets away from one of heads.
2249
2277
2250 Returns:
2278 Returns:
2251 A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
2279 A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
2252
2280
2253 visitnodes: The list of nodes (either full or ellipsis) which
2281 visitnodes: The list of nodes (either full or ellipsis) which
2254 need to be sent to the client.
2282 need to be sent to the client.
2255 relevant_nodes: The set of changelog nodes which change a file inside
2283 relevant_nodes: The set of changelog nodes which change a file inside
2256 the narrowspec. The client needs these as non-ellipsis nodes.
2284 the narrowspec. The client needs these as non-ellipsis nodes.
2257 ellipsisroots: A dict of {rev: parents} that is used in
2285 ellipsisroots: A dict of {rev: parents} that is used in
2258 narrowchangegroup to produce ellipsis nodes with the
2286 narrowchangegroup to produce ellipsis nodes with the
2259 correct parents.
2287 correct parents.
2260 """
2288 """
2261 cl = repo.changelog
2289 cl = repo.changelog
2262 mfl = repo.manifestlog
2290 mfl = repo.manifestlog
2263
2291
2264 clrev = cl.rev
2292 clrev = cl.rev
2265
2293
2266 commonrevs = {clrev(n) for n in common} | {nullrev}
2294 commonrevs = {clrev(n) for n in common} | {nullrev}
2267 headsrevs = {clrev(n) for n in heads}
2295 headsrevs = {clrev(n) for n in heads}
2268
2296
2269 if depth:
2297 if depth:
2270 revdepth = {h: 0 for h in headsrevs}
2298 revdepth = {h: 0 for h in headsrevs}
2271
2299
2272 ellipsisheads = collections.defaultdict(set)
2300 ellipsisheads = collections.defaultdict(set)
2273 ellipsisroots = collections.defaultdict(set)
2301 ellipsisroots = collections.defaultdict(set)
2274
2302
2275 def addroot(head, curchange):
2303 def addroot(head, curchange):
2276 """Add a root to an ellipsis head, splitting heads with 3 roots."""
2304 """Add a root to an ellipsis head, splitting heads with 3 roots."""
2277 ellipsisroots[head].add(curchange)
2305 ellipsisroots[head].add(curchange)
2278 # Recursively split ellipsis heads with 3 roots by finding the
2306 # Recursively split ellipsis heads with 3 roots by finding the
2279 # roots' youngest common descendant which is an elided merge commit.
2307 # roots' youngest common descendant which is an elided merge commit.
2280 # That descendant takes 2 of the 3 roots as its own, and becomes a
2308 # That descendant takes 2 of the 3 roots as its own, and becomes a
2281 # root of the head.
2309 # root of the head.
2282 while len(ellipsisroots[head]) > 2:
2310 while len(ellipsisroots[head]) > 2:
2283 child, roots = splithead(head)
2311 child, roots = splithead(head)
2284 splitroots(head, child, roots)
2312 splitroots(head, child, roots)
2285 head = child # Recurse in case we just added a 3rd root
2313 head = child # Recurse in case we just added a 3rd root
2286
2314
2287 def splitroots(head, child, roots):
2315 def splitroots(head, child, roots):
2288 ellipsisroots[head].difference_update(roots)
2316 ellipsisroots[head].difference_update(roots)
2289 ellipsisroots[head].add(child)
2317 ellipsisroots[head].add(child)
2290 ellipsisroots[child].update(roots)
2318 ellipsisroots[child].update(roots)
2291 ellipsisroots[child].discard(child)
2319 ellipsisroots[child].discard(child)
2292
2320
2293 def splithead(head):
2321 def splithead(head):
2294 r1, r2, r3 = sorted(ellipsisroots[head])
2322 r1, r2, r3 = sorted(ellipsisroots[head])
2295 for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
2323 for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
2296 mid = repo.revs(
2324 mid = repo.revs(
2297 b'sort(merge() & %d::%d & %d::%d, -rev)', nr1, head, nr2, head
2325 b'sort(merge() & %d::%d & %d::%d, -rev)', nr1, head, nr2, head
2298 )
2326 )
2299 for j in mid:
2327 for j in mid:
2300 if j == nr2:
2328 if j == nr2:
2301 return nr2, (nr1, nr2)
2329 return nr2, (nr1, nr2)
2302 if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
2330 if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
2303 return j, (nr1, nr2)
2331 return j, (nr1, nr2)
2304 raise error.Abort(
2332 raise error.Abort(
2305 _(
2333 _(
2306 b'Failed to split up ellipsis node! head: %d, '
2334 b'Failed to split up ellipsis node! head: %d, '
2307 b'roots: %d %d %d'
2335 b'roots: %d %d %d'
2308 )
2336 )
2309 % (head, r1, r2, r3)
2337 % (head, r1, r2, r3)
2310 )
2338 )
2311
2339
2312 missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
2340 missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
2313 visit = reversed(missing)
2341 visit = reversed(missing)
2314 relevant_nodes = set()
2342 relevant_nodes = set()
2315 visitnodes = [cl.node(m) for m in missing]
2343 visitnodes = [cl.node(m) for m in missing]
2316 required = set(headsrevs) | known
2344 required = set(headsrevs) | known
2317 for rev in visit:
2345 for rev in visit:
2318 clrev = cl.changelogrevision(rev)
2346 clrev = cl.changelogrevision(rev)
2319 ps = [prev for prev in cl.parentrevs(rev) if prev != nullrev]
2347 ps = [prev for prev in cl.parentrevs(rev) if prev != nullrev]
2320 if depth is not None:
2348 if depth is not None:
2321 curdepth = revdepth[rev]
2349 curdepth = revdepth[rev]
2322 for p in ps:
2350 for p in ps:
2323 revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
2351 revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
2324 needed = False
2352 needed = False
2325 shallow_enough = depth is None or revdepth[rev] <= depth
2353 shallow_enough = depth is None or revdepth[rev] <= depth
2326 if shallow_enough:
2354 if shallow_enough:
2327 curmf = mfl[clrev.manifest].read()
2355 curmf = mfl[clrev.manifest].read()
2328 if ps:
2356 if ps:
2329 # We choose to not trust the changed files list in
2357 # We choose to not trust the changed files list in
2330 # changesets because it's not always correct. TODO: could
2358 # changesets because it's not always correct. TODO: could
2331 # we trust it for the non-merge case?
2359 # we trust it for the non-merge case?
2332 p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
2360 p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
2333 needed = bool(curmf.diff(p1mf, match))
2361 needed = bool(curmf.diff(p1mf, match))
2334 if not needed and len(ps) > 1:
2362 if not needed and len(ps) > 1:
2335 # For merge changes, the list of changed files is not
2363 # For merge changes, the list of changed files is not
2336 # helpful, since we need to emit the merge if a file
2364 # helpful, since we need to emit the merge if a file
2337 # in the narrow spec has changed on either side of the
2365 # in the narrow spec has changed on either side of the
2338 # merge. As a result, we do a manifest diff to check.
2366 # merge. As a result, we do a manifest diff to check.
2339 p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
2367 p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
2340 needed = bool(curmf.diff(p2mf, match))
2368 needed = bool(curmf.diff(p2mf, match))
2341 else:
2369 else:
2342 # For a root node, we need to include the node if any
2370 # For a root node, we need to include the node if any
2343 # files in the node match the narrowspec.
2371 # files in the node match the narrowspec.
2344 needed = any(curmf.walk(match))
2372 needed = any(curmf.walk(match))
2345
2373
2346 if needed:
2374 if needed:
2347 for head in ellipsisheads[rev]:
2375 for head in ellipsisheads[rev]:
2348 addroot(head, rev)
2376 addroot(head, rev)
2349 for p in ps:
2377 for p in ps:
2350 required.add(p)
2378 required.add(p)
2351 relevant_nodes.add(cl.node(rev))
2379 relevant_nodes.add(cl.node(rev))
2352 else:
2380 else:
2353 if not ps:
2381 if not ps:
2354 ps = [nullrev]
2382 ps = [nullrev]
2355 if rev in required:
2383 if rev in required:
2356 for head in ellipsisheads[rev]:
2384 for head in ellipsisheads[rev]:
2357 addroot(head, rev)
2385 addroot(head, rev)
2358 for p in ps:
2386 for p in ps:
2359 ellipsisheads[p].add(rev)
2387 ellipsisheads[p].add(rev)
2360 else:
2388 else:
2361 for p in ps:
2389 for p in ps:
2362 ellipsisheads[p] |= ellipsisheads[rev]
2390 ellipsisheads[p] |= ellipsisheads[rev]
2363
2391
2364 # add common changesets as roots of their reachable ellipsis heads
2392 # add common changesets as roots of their reachable ellipsis heads
2365 for c in commonrevs:
2393 for c in commonrevs:
2366 for head in ellipsisheads[c]:
2394 for head in ellipsisheads[c]:
2367 addroot(head, c)
2395 addroot(head, c)
2368 return visitnodes, relevant_nodes, ellipsisroots
2396 return visitnodes, relevant_nodes, ellipsisroots
2369
2397
2370
2398
2371 def caps20to10(repo, role):
2399 def caps20to10(repo, role):
2372 """return a set with appropriate options to use bundle20 during getbundle"""
2400 """return a set with appropriate options to use bundle20 during getbundle"""
2373 caps = {b'HG20'}
2401 caps = {b'HG20'}
2374 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=role))
2402 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=role))
2375 caps.add(b'bundle2=' + urlreq.quote(capsblob))
2403 caps.add(b'bundle2=' + urlreq.quote(capsblob))
2376 return caps
2404 return caps
2377
2405
2378
2406
2379 # List of names of steps to perform for a bundle2 for getbundle, order matters.
2407 # List of names of steps to perform for a bundle2 for getbundle, order matters.
2380 getbundle2partsorder = []
2408 getbundle2partsorder = []
2381
2409
2382 # Mapping between step name and function
2410 # Mapping between step name and function
2383 #
2411 #
2384 # This exists to help extensions wrap steps if necessary
2412 # This exists to help extensions wrap steps if necessary
2385 getbundle2partsmapping = {}
2413 getbundle2partsmapping = {}
2386
2414
2387
2415
2388 def getbundle2partsgenerator(stepname, idx=None):
2416 def getbundle2partsgenerator(stepname, idx=None):
2389 """decorator for function generating bundle2 part for getbundle
2417 """decorator for function generating bundle2 part for getbundle
2390
2418
2391 The function is added to the step -> function mapping and appended to the
2419 The function is added to the step -> function mapping and appended to the
2392 list of steps. Beware that decorated functions will be added in order
2420 list of steps. Beware that decorated functions will be added in order
2393 (this may matter).
2421 (this may matter).
2394
2422
2395 You can only use this decorator for new steps, if you want to wrap a step
2423 You can only use this decorator for new steps, if you want to wrap a step
2396 from an extension, attack the getbundle2partsmapping dictionary directly."""
2424 from an extension, attack the getbundle2partsmapping dictionary directly."""
2397
2425
2398 def dec(func):
2426 def dec(func):
2399 assert stepname not in getbundle2partsmapping
2427 assert stepname not in getbundle2partsmapping
2400 getbundle2partsmapping[stepname] = func
2428 getbundle2partsmapping[stepname] = func
2401 if idx is None:
2429 if idx is None:
2402 getbundle2partsorder.append(stepname)
2430 getbundle2partsorder.append(stepname)
2403 else:
2431 else:
2404 getbundle2partsorder.insert(idx, stepname)
2432 getbundle2partsorder.insert(idx, stepname)
2405 return func
2433 return func
2406
2434
2407 return dec
2435 return dec
2408
2436
2409
2437
2410 def bundle2requested(bundlecaps):
2438 def bundle2requested(bundlecaps):
2411 if bundlecaps is not None:
2439 if bundlecaps is not None:
2412 return any(cap.startswith(b'HG2') for cap in bundlecaps)
2440 return any(cap.startswith(b'HG2') for cap in bundlecaps)
2413 return False
2441 return False
2414
2442
2415
2443
2416 def getbundlechunks(
2444 def getbundlechunks(
2417 repo, source, heads=None, common=None, bundlecaps=None, **kwargs
2445 repo, source, heads=None, common=None, bundlecaps=None, **kwargs
2418 ):
2446 ):
2419 """Return chunks constituting a bundle's raw data.
2447 """Return chunks constituting a bundle's raw data.
2420
2448
2421 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
2449 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
2422 passed.
2450 passed.
2423
2451
2424 Returns a 2-tuple of a dict with metadata about the generated bundle
2452 Returns a 2-tuple of a dict with metadata about the generated bundle
2425 and an iterator over raw chunks (of varying sizes).
2453 and an iterator over raw chunks (of varying sizes).
2426 """
2454 """
2427 kwargs = pycompat.byteskwargs(kwargs)
2455 kwargs = pycompat.byteskwargs(kwargs)
2428 info = {}
2456 info = {}
2429 usebundle2 = bundle2requested(bundlecaps)
2457 usebundle2 = bundle2requested(bundlecaps)
2430 # bundle10 case
2458 # bundle10 case
2431 if not usebundle2:
2459 if not usebundle2:
2432 if bundlecaps and not kwargs.get(b'cg', True):
2460 if bundlecaps and not kwargs.get(b'cg', True):
2433 raise ValueError(
2461 raise ValueError(
2434 _(b'request for bundle10 must include changegroup')
2462 _(b'request for bundle10 must include changegroup')
2435 )
2463 )
2436
2464
2437 if kwargs:
2465 if kwargs:
2438 raise ValueError(
2466 raise ValueError(
2439 _(b'unsupported getbundle arguments: %s')
2467 _(b'unsupported getbundle arguments: %s')
2440 % b', '.join(sorted(kwargs.keys()))
2468 % b', '.join(sorted(kwargs.keys()))
2441 )
2469 )
2442 outgoing = _computeoutgoing(repo, heads, common)
2470 outgoing = _computeoutgoing(repo, heads, common)
2443 info[b'bundleversion'] = 1
2471 info[b'bundleversion'] = 1
2444 return (
2472 return (
2445 info,
2473 info,
2446 changegroup.makestream(
2474 changegroup.makestream(
2447 repo, outgoing, b'01', source, bundlecaps=bundlecaps
2475 repo, outgoing, b'01', source, bundlecaps=bundlecaps
2448 ),
2476 ),
2449 )
2477 )
2450
2478
2451 # bundle20 case
2479 # bundle20 case
2452 info[b'bundleversion'] = 2
2480 info[b'bundleversion'] = 2
2453 b2caps = {}
2481 b2caps = {}
2454 for bcaps in bundlecaps:
2482 for bcaps in bundlecaps:
2455 if bcaps.startswith(b'bundle2='):
2483 if bcaps.startswith(b'bundle2='):
2456 blob = urlreq.unquote(bcaps[len(b'bundle2=') :])
2484 blob = urlreq.unquote(bcaps[len(b'bundle2=') :])
2457 b2caps.update(bundle2.decodecaps(blob))
2485 b2caps.update(bundle2.decodecaps(blob))
2458 bundler = bundle2.bundle20(repo.ui, b2caps)
2486 bundler = bundle2.bundle20(repo.ui, b2caps)
2459
2487
2460 kwargs[b'heads'] = heads
2488 kwargs[b'heads'] = heads
2461 kwargs[b'common'] = common
2489 kwargs[b'common'] = common
2462
2490
2463 for name in getbundle2partsorder:
2491 for name in getbundle2partsorder:
2464 func = getbundle2partsmapping[name]
2492 func = getbundle2partsmapping[name]
2465 func(
2493 func(
2466 bundler,
2494 bundler,
2467 repo,
2495 repo,
2468 source,
2496 source,
2469 bundlecaps=bundlecaps,
2497 bundlecaps=bundlecaps,
2470 b2caps=b2caps,
2498 b2caps=b2caps,
2471 **pycompat.strkwargs(kwargs)
2499 **pycompat.strkwargs(kwargs)
2472 )
2500 )
2473
2501
2474 info[b'prefercompressed'] = bundler.prefercompressed
2502 info[b'prefercompressed'] = bundler.prefercompressed
2475
2503
2476 return info, bundler.getchunks()
2504 return info, bundler.getchunks()
2477
2505
2478
2506
2479 @getbundle2partsgenerator(b'stream2')
2507 @getbundle2partsgenerator(b'stream2')
2480 def _getbundlestream2(bundler, repo, *args, **kwargs):
2508 def _getbundlestream2(bundler, repo, *args, **kwargs):
2481 return bundle2.addpartbundlestream2(bundler, repo, **kwargs)
2509 return bundle2.addpartbundlestream2(bundler, repo, **kwargs)
2482
2510
2483
2511
2484 @getbundle2partsgenerator(b'changegroup')
2512 @getbundle2partsgenerator(b'changegroup')
2485 def _getbundlechangegrouppart(
2513 def _getbundlechangegrouppart(
2486 bundler,
2514 bundler,
2487 repo,
2515 repo,
2488 source,
2516 source,
2489 bundlecaps=None,
2517 bundlecaps=None,
2490 b2caps=None,
2518 b2caps=None,
2491 heads=None,
2519 heads=None,
2492 common=None,
2520 common=None,
2493 **kwargs
2521 **kwargs
2494 ):
2522 ):
2495 """add a changegroup part to the requested bundle"""
2523 """add a changegroup part to the requested bundle"""
2496 if not kwargs.get('cg', True) or not b2caps:
2524 if not kwargs.get('cg', True) or not b2caps:
2497 return
2525 return
2498
2526
2499 version = b'01'
2527 version = b'01'
2500 cgversions = b2caps.get(b'changegroup')
2528 cgversions = b2caps.get(b'changegroup')
2501 if cgversions: # 3.1 and 3.2 ship with an empty value
2529 if cgversions: # 3.1 and 3.2 ship with an empty value
2502 cgversions = [
2530 cgversions = [
2503 v
2531 v
2504 for v in cgversions
2532 for v in cgversions
2505 if v in changegroup.supportedoutgoingversions(repo)
2533 if v in changegroup.supportedoutgoingversions(repo)
2506 ]
2534 ]
2507 if not cgversions:
2535 if not cgversions:
2508 raise error.Abort(_(b'no common changegroup version'))
2536 raise error.Abort(_(b'no common changegroup version'))
2509 version = max(cgversions)
2537 version = max(cgversions)
2510
2538
2511 outgoing = _computeoutgoing(repo, heads, common)
2539 outgoing = _computeoutgoing(repo, heads, common)
2512 if not outgoing.missing:
2540 if not outgoing.missing:
2513 return
2541 return
2514
2542
2515 if kwargs.get('narrow', False):
2543 if kwargs.get('narrow', False):
2516 include = sorted(filter(bool, kwargs.get('includepats', [])))
2544 include = sorted(filter(bool, kwargs.get('includepats', [])))
2517 exclude = sorted(filter(bool, kwargs.get('excludepats', [])))
2545 exclude = sorted(filter(bool, kwargs.get('excludepats', [])))
2518 matcher = narrowspec.match(repo.root, include=include, exclude=exclude)
2546 matcher = narrowspec.match(repo.root, include=include, exclude=exclude)
2519 else:
2547 else:
2520 matcher = None
2548 matcher = None
2521
2549
2522 cgstream = changegroup.makestream(
2550 cgstream = changegroup.makestream(
2523 repo, outgoing, version, source, bundlecaps=bundlecaps, matcher=matcher
2551 repo, outgoing, version, source, bundlecaps=bundlecaps, matcher=matcher
2524 )
2552 )
2525
2553
2526 part = bundler.newpart(b'changegroup', data=cgstream)
2554 part = bundler.newpart(b'changegroup', data=cgstream)
2527 if cgversions:
2555 if cgversions:
2528 part.addparam(b'version', version)
2556 part.addparam(b'version', version)
2529
2557
2530 part.addparam(b'nbchanges', b'%d' % len(outgoing.missing), mandatory=False)
2558 part.addparam(b'nbchanges', b'%d' % len(outgoing.missing), mandatory=False)
2531
2559
2532 if b'treemanifest' in repo.requirements:
2560 if b'treemanifest' in repo.requirements:
2533 part.addparam(b'treemanifest', b'1')
2561 part.addparam(b'treemanifest', b'1')
2534
2562
2535 if b'exp-sidedata-flag' in repo.requirements:
2563 if b'exp-sidedata-flag' in repo.requirements:
2536 part.addparam(b'exp-sidedata', b'1')
2564 part.addparam(b'exp-sidedata', b'1')
2537
2565
2538 if (
2566 if (
2539 kwargs.get('narrow', False)
2567 kwargs.get('narrow', False)
2540 and kwargs.get('narrow_acl', False)
2568 and kwargs.get('narrow_acl', False)
2541 and (include or exclude)
2569 and (include or exclude)
2542 ):
2570 ):
2543 # this is mandatory because otherwise ACL clients won't work
2571 # this is mandatory because otherwise ACL clients won't work
2544 narrowspecpart = bundler.newpart(b'Narrow:responsespec')
2572 narrowspecpart = bundler.newpart(b'Narrow:responsespec')
2545 narrowspecpart.data = b'%s\0%s' % (
2573 narrowspecpart.data = b'%s\0%s' % (
2546 b'\n'.join(include),
2574 b'\n'.join(include),
2547 b'\n'.join(exclude),
2575 b'\n'.join(exclude),
2548 )
2576 )
2549
2577
2550
2578
2551 @getbundle2partsgenerator(b'bookmarks')
2579 @getbundle2partsgenerator(b'bookmarks')
2552 def _getbundlebookmarkpart(
2580 def _getbundlebookmarkpart(
2553 bundler, repo, source, bundlecaps=None, b2caps=None, **kwargs
2581 bundler, repo, source, bundlecaps=None, b2caps=None, **kwargs
2554 ):
2582 ):
2555 """add a bookmark part to the requested bundle"""
2583 """add a bookmark part to the requested bundle"""
2556 if not kwargs.get('bookmarks', False):
2584 if not kwargs.get('bookmarks', False):
2557 return
2585 return
2558 if not b2caps or b'bookmarks' not in b2caps:
2586 if not b2caps or b'bookmarks' not in b2caps:
2559 raise error.Abort(_(b'no common bookmarks exchange method'))
2587 raise error.Abort(_(b'no common bookmarks exchange method'))
2560 books = bookmod.listbinbookmarks(repo)
2588 books = bookmod.listbinbookmarks(repo)
2561 data = bookmod.binaryencode(books)
2589 data = bookmod.binaryencode(books)
2562 if data:
2590 if data:
2563 bundler.newpart(b'bookmarks', data=data)
2591 bundler.newpart(b'bookmarks', data=data)
2564
2592
2565
2593
2566 @getbundle2partsgenerator(b'listkeys')
2594 @getbundle2partsgenerator(b'listkeys')
2567 def _getbundlelistkeysparts(
2595 def _getbundlelistkeysparts(
2568 bundler, repo, source, bundlecaps=None, b2caps=None, **kwargs
2596 bundler, repo, source, bundlecaps=None, b2caps=None, **kwargs
2569 ):
2597 ):
2570 """add parts containing listkeys namespaces to the requested bundle"""
2598 """add parts containing listkeys namespaces to the requested bundle"""
2571 listkeys = kwargs.get('listkeys', ())
2599 listkeys = kwargs.get('listkeys', ())
2572 for namespace in listkeys:
2600 for namespace in listkeys:
2573 part = bundler.newpart(b'listkeys')
2601 part = bundler.newpart(b'listkeys')
2574 part.addparam(b'namespace', namespace)
2602 part.addparam(b'namespace', namespace)
2575 keys = repo.listkeys(namespace).items()
2603 keys = repo.listkeys(namespace).items()
2576 part.data = pushkey.encodekeys(keys)
2604 part.data = pushkey.encodekeys(keys)
2577
2605
2578
2606
2579 @getbundle2partsgenerator(b'obsmarkers')
2607 @getbundle2partsgenerator(b'obsmarkers')
2580 def _getbundleobsmarkerpart(
2608 def _getbundleobsmarkerpart(
2581 bundler, repo, source, bundlecaps=None, b2caps=None, heads=None, **kwargs
2609 bundler, repo, source, bundlecaps=None, b2caps=None, heads=None, **kwargs
2582 ):
2610 ):
2583 """add an obsolescence markers part to the requested bundle"""
2611 """add an obsolescence markers part to the requested bundle"""
2584 if kwargs.get('obsmarkers', False):
2612 if kwargs.get('obsmarkers', False):
2585 if heads is None:
2613 if heads is None:
2586 heads = repo.heads()
2614 heads = repo.heads()
2587 subset = [c.node() for c in repo.set(b'::%ln', heads)]
2615 subset = [c.node() for c in repo.set(b'::%ln', heads)]
2588 markers = repo.obsstore.relevantmarkers(subset)
2616 markers = repo.obsstore.relevantmarkers(subset)
2589 markers = obsutil.sortedmarkers(markers)
2617 markers = obsutil.sortedmarkers(markers)
2590 bundle2.buildobsmarkerspart(bundler, markers)
2618 bundle2.buildobsmarkerspart(bundler, markers)
2591
2619
2592
2620
2593 @getbundle2partsgenerator(b'phases')
2621 @getbundle2partsgenerator(b'phases')
2594 def _getbundlephasespart(
2622 def _getbundlephasespart(
2595 bundler, repo, source, bundlecaps=None, b2caps=None, heads=None, **kwargs
2623 bundler, repo, source, bundlecaps=None, b2caps=None, heads=None, **kwargs
2596 ):
2624 ):
2597 """add phase heads part to the requested bundle"""
2625 """add phase heads part to the requested bundle"""
2598 if kwargs.get('phases', False):
2626 if kwargs.get('phases', False):
2599 if not b2caps or b'heads' not in b2caps.get(b'phases'):
2627 if not b2caps or b'heads' not in b2caps.get(b'phases'):
2600 raise error.Abort(_(b'no common phases exchange method'))
2628 raise error.Abort(_(b'no common phases exchange method'))
2601 if heads is None:
2629 if heads is None:
2602 heads = repo.heads()
2630 heads = repo.heads()
2603
2631
2604 headsbyphase = collections.defaultdict(set)
2632 headsbyphase = collections.defaultdict(set)
2605 if repo.publishing():
2633 if repo.publishing():
2606 headsbyphase[phases.public] = heads
2634 headsbyphase[phases.public] = heads
2607 else:
2635 else:
2608 # find the appropriate heads to move
2636 # find the appropriate heads to move
2609
2637
2610 phase = repo._phasecache.phase
2638 phase = repo._phasecache.phase
2611 node = repo.changelog.node
2639 node = repo.changelog.node
2612 rev = repo.changelog.rev
2640 rev = repo.changelog.rev
2613 for h in heads:
2641 for h in heads:
2614 headsbyphase[phase(repo, rev(h))].add(h)
2642 headsbyphase[phase(repo, rev(h))].add(h)
2615 seenphases = list(headsbyphase.keys())
2643 seenphases = list(headsbyphase.keys())
2616
2644
2617 # We do not handle anything but public and draft phase for now)
2645 # We do not handle anything but public and draft phase for now)
2618 if seenphases:
2646 if seenphases:
2619 assert max(seenphases) <= phases.draft
2647 assert max(seenphases) <= phases.draft
2620
2648
2621 # if client is pulling non-public changesets, we need to find
2649 # if client is pulling non-public changesets, we need to find
2622 # intermediate public heads.
2650 # intermediate public heads.
2623 draftheads = headsbyphase.get(phases.draft, set())
2651 draftheads = headsbyphase.get(phases.draft, set())
2624 if draftheads:
2652 if draftheads:
2625 publicheads = headsbyphase.get(phases.public, set())
2653 publicheads = headsbyphase.get(phases.public, set())
2626
2654
2627 revset = b'heads(only(%ln, %ln) and public())'
2655 revset = b'heads(only(%ln, %ln) and public())'
2628 extraheads = repo.revs(revset, draftheads, publicheads)
2656 extraheads = repo.revs(revset, draftheads, publicheads)
2629 for r in extraheads:
2657 for r in extraheads:
2630 headsbyphase[phases.public].add(node(r))
2658 headsbyphase[phases.public].add(node(r))
2631
2659
2632 # transform data in a format used by the encoding function
2660 # transform data in a format used by the encoding function
2633 phasemapping = []
2661 phasemapping = []
2634 for phase in phases.allphases:
2662 for phase in phases.allphases:
2635 phasemapping.append(sorted(headsbyphase[phase]))
2663 phasemapping.append(sorted(headsbyphase[phase]))
2636
2664
2637 # generate the actual part
2665 # generate the actual part
2638 phasedata = phases.binaryencode(phasemapping)
2666 phasedata = phases.binaryencode(phasemapping)
2639 bundler.newpart(b'phase-heads', data=phasedata)
2667 bundler.newpart(b'phase-heads', data=phasedata)
2640
2668
2641
2669
2642 @getbundle2partsgenerator(b'hgtagsfnodes')
2670 @getbundle2partsgenerator(b'hgtagsfnodes')
2643 def _getbundletagsfnodes(
2671 def _getbundletagsfnodes(
2644 bundler,
2672 bundler,
2645 repo,
2673 repo,
2646 source,
2674 source,
2647 bundlecaps=None,
2675 bundlecaps=None,
2648 b2caps=None,
2676 b2caps=None,
2649 heads=None,
2677 heads=None,
2650 common=None,
2678 common=None,
2651 **kwargs
2679 **kwargs
2652 ):
2680 ):
2653 """Transfer the .hgtags filenodes mapping.
2681 """Transfer the .hgtags filenodes mapping.
2654
2682
2655 Only values for heads in this bundle will be transferred.
2683 Only values for heads in this bundle will be transferred.
2656
2684
2657 The part data consists of pairs of 20 byte changeset node and .hgtags
2685 The part data consists of pairs of 20 byte changeset node and .hgtags
2658 filenodes raw values.
2686 filenodes raw values.
2659 """
2687 """
2660 # Don't send unless:
2688 # Don't send unless:
2661 # - changeset are being exchanged,
2689 # - changeset are being exchanged,
2662 # - the client supports it.
2690 # - the client supports it.
2663 if not b2caps or not (kwargs.get('cg', True) and b'hgtagsfnodes' in b2caps):
2691 if not b2caps or not (kwargs.get('cg', True) and b'hgtagsfnodes' in b2caps):
2664 return
2692 return
2665
2693
2666 outgoing = _computeoutgoing(repo, heads, common)
2694 outgoing = _computeoutgoing(repo, heads, common)
2667 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
2695 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
2668
2696
2669
2697
2670 @getbundle2partsgenerator(b'cache:rev-branch-cache')
2698 @getbundle2partsgenerator(b'cache:rev-branch-cache')
2671 def _getbundlerevbranchcache(
2699 def _getbundlerevbranchcache(
2672 bundler,
2700 bundler,
2673 repo,
2701 repo,
2674 source,
2702 source,
2675 bundlecaps=None,
2703 bundlecaps=None,
2676 b2caps=None,
2704 b2caps=None,
2677 heads=None,
2705 heads=None,
2678 common=None,
2706 common=None,
2679 **kwargs
2707 **kwargs
2680 ):
2708 ):
2681 """Transfer the rev-branch-cache mapping
2709 """Transfer the rev-branch-cache mapping
2682
2710
2683 The payload is a series of data related to each branch
2711 The payload is a series of data related to each branch
2684
2712
2685 1) branch name length
2713 1) branch name length
2686 2) number of open heads
2714 2) number of open heads
2687 3) number of closed heads
2715 3) number of closed heads
2688 4) open heads nodes
2716 4) open heads nodes
2689 5) closed heads nodes
2717 5) closed heads nodes
2690 """
2718 """
2691 # Don't send unless:
2719 # Don't send unless:
2692 # - changeset are being exchanged,
2720 # - changeset are being exchanged,
2693 # - the client supports it.
2721 # - the client supports it.
2694 # - narrow bundle isn't in play (not currently compatible).
2722 # - narrow bundle isn't in play (not currently compatible).
2695 if (
2723 if (
2696 not kwargs.get('cg', True)
2724 not kwargs.get('cg', True)
2697 or not b2caps
2725 or not b2caps
2698 or b'rev-branch-cache' not in b2caps
2726 or b'rev-branch-cache' not in b2caps
2699 or kwargs.get('narrow', False)
2727 or kwargs.get('narrow', False)
2700 or repo.ui.has_section(_NARROWACL_SECTION)
2728 or repo.ui.has_section(_NARROWACL_SECTION)
2701 ):
2729 ):
2702 return
2730 return
2703
2731
2704 outgoing = _computeoutgoing(repo, heads, common)
2732 outgoing = _computeoutgoing(repo, heads, common)
2705 bundle2.addpartrevbranchcache(repo, bundler, outgoing)
2733 bundle2.addpartrevbranchcache(repo, bundler, outgoing)
2706
2734
2707
2735
2708 def check_heads(repo, their_heads, context):
2736 def check_heads(repo, their_heads, context):
2709 """check if the heads of a repo have been modified
2737 """check if the heads of a repo have been modified
2710
2738
2711 Used by peer for unbundling.
2739 Used by peer for unbundling.
2712 """
2740 """
2713 heads = repo.heads()
2741 heads = repo.heads()
2714 heads_hash = hashutil.sha1(b''.join(sorted(heads))).digest()
2742 heads_hash = hashutil.sha1(b''.join(sorted(heads))).digest()
2715 if not (
2743 if not (
2716 their_heads == [b'force']
2744 their_heads == [b'force']
2717 or their_heads == heads
2745 or their_heads == heads
2718 or their_heads == [b'hashed', heads_hash]
2746 or their_heads == [b'hashed', heads_hash]
2719 ):
2747 ):
2720 # someone else committed/pushed/unbundled while we
2748 # someone else committed/pushed/unbundled while we
2721 # were transferring data
2749 # were transferring data
2722 raise error.PushRaced(
2750 raise error.PushRaced(
2723 b'repository changed while %s - please try again' % context
2751 b'repository changed while %s - please try again' % context
2724 )
2752 )
2725
2753
2726
2754
2727 def unbundle(repo, cg, heads, source, url):
2755 def unbundle(repo, cg, heads, source, url):
2728 """Apply a bundle to a repo.
2756 """Apply a bundle to a repo.
2729
2757
2730 this function makes sure the repo is locked during the application and have
2758 this function makes sure the repo is locked during the application and have
2731 mechanism to check that no push race occurred between the creation of the
2759 mechanism to check that no push race occurred between the creation of the
2732 bundle and its application.
2760 bundle and its application.
2733
2761
2734 If the push was raced as PushRaced exception is raised."""
2762 If the push was raced as PushRaced exception is raised."""
2735 r = 0
2763 r = 0
2736 # need a transaction when processing a bundle2 stream
2764 # need a transaction when processing a bundle2 stream
2737 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
2765 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
2738 lockandtr = [None, None, None]
2766 lockandtr = [None, None, None]
2739 recordout = None
2767 recordout = None
2740 # quick fix for output mismatch with bundle2 in 3.4
2768 # quick fix for output mismatch with bundle2 in 3.4
2741 captureoutput = repo.ui.configbool(
2769 captureoutput = repo.ui.configbool(
2742 b'experimental', b'bundle2-output-capture'
2770 b'experimental', b'bundle2-output-capture'
2743 )
2771 )
2744 if url.startswith(b'remote:http:') or url.startswith(b'remote:https:'):
2772 if url.startswith(b'remote:http:') or url.startswith(b'remote:https:'):
2745 captureoutput = True
2773 captureoutput = True
2746 try:
2774 try:
2747 # note: outside bundle1, 'heads' is expected to be empty and this
2775 # note: outside bundle1, 'heads' is expected to be empty and this
2748 # 'check_heads' call wil be a no-op
2776 # 'check_heads' call wil be a no-op
2749 check_heads(repo, heads, b'uploading changes')
2777 check_heads(repo, heads, b'uploading changes')
2750 # push can proceed
2778 # push can proceed
2751 if not isinstance(cg, bundle2.unbundle20):
2779 if not isinstance(cg, bundle2.unbundle20):
2752 # legacy case: bundle1 (changegroup 01)
2780 # legacy case: bundle1 (changegroup 01)
2753 txnname = b"\n".join([source, util.hidepassword(url)])
2781 txnname = b"\n".join([source, util.hidepassword(url)])
2754 with repo.lock(), repo.transaction(txnname) as tr:
2782 with repo.lock(), repo.transaction(txnname) as tr:
2755 op = bundle2.applybundle(repo, cg, tr, source, url)
2783 op = bundle2.applybundle(repo, cg, tr, source, url)
2756 r = bundle2.combinechangegroupresults(op)
2784 r = bundle2.combinechangegroupresults(op)
2757 else:
2785 else:
2758 r = None
2786 r = None
2759 try:
2787 try:
2760
2788
2761 def gettransaction():
2789 def gettransaction():
2762 if not lockandtr[2]:
2790 if not lockandtr[2]:
2763 if not bookmod.bookmarksinstore(repo):
2791 if not bookmod.bookmarksinstore(repo):
2764 lockandtr[0] = repo.wlock()
2792 lockandtr[0] = repo.wlock()
2765 lockandtr[1] = repo.lock()
2793 lockandtr[1] = repo.lock()
2766 lockandtr[2] = repo.transaction(source)
2794 lockandtr[2] = repo.transaction(source)
2767 lockandtr[2].hookargs[b'source'] = source
2795 lockandtr[2].hookargs[b'source'] = source
2768 lockandtr[2].hookargs[b'url'] = url
2796 lockandtr[2].hookargs[b'url'] = url
2769 lockandtr[2].hookargs[b'bundle2'] = b'1'
2797 lockandtr[2].hookargs[b'bundle2'] = b'1'
2770 return lockandtr[2]
2798 return lockandtr[2]
2771
2799
2772 # Do greedy locking by default until we're satisfied with lazy
2800 # Do greedy locking by default until we're satisfied with lazy
2773 # locking.
2801 # locking.
2774 if not repo.ui.configbool(
2802 if not repo.ui.configbool(
2775 b'experimental', b'bundle2lazylocking'
2803 b'experimental', b'bundle2lazylocking'
2776 ):
2804 ):
2777 gettransaction()
2805 gettransaction()
2778
2806
2779 op = bundle2.bundleoperation(
2807 op = bundle2.bundleoperation(
2780 repo,
2808 repo,
2781 gettransaction,
2809 gettransaction,
2782 captureoutput=captureoutput,
2810 captureoutput=captureoutput,
2783 source=b'push',
2811 source=b'push',
2784 )
2812 )
2785 try:
2813 try:
2786 op = bundle2.processbundle(repo, cg, op=op)
2814 op = bundle2.processbundle(repo, cg, op=op)
2787 finally:
2815 finally:
2788 r = op.reply
2816 r = op.reply
2789 if captureoutput and r is not None:
2817 if captureoutput and r is not None:
2790 repo.ui.pushbuffer(error=True, subproc=True)
2818 repo.ui.pushbuffer(error=True, subproc=True)
2791
2819
2792 def recordout(output):
2820 def recordout(output):
2793 r.newpart(b'output', data=output, mandatory=False)
2821 r.newpart(b'output', data=output, mandatory=False)
2794
2822
2795 if lockandtr[2] is not None:
2823 if lockandtr[2] is not None:
2796 lockandtr[2].close()
2824 lockandtr[2].close()
2797 except BaseException as exc:
2825 except BaseException as exc:
2798 exc.duringunbundle2 = True
2826 exc.duringunbundle2 = True
2799 if captureoutput and r is not None:
2827 if captureoutput and r is not None:
2800 parts = exc._bundle2salvagedoutput = r.salvageoutput()
2828 parts = exc._bundle2salvagedoutput = r.salvageoutput()
2801
2829
2802 def recordout(output):
2830 def recordout(output):
2803 part = bundle2.bundlepart(
2831 part = bundle2.bundlepart(
2804 b'output', data=output, mandatory=False
2832 b'output', data=output, mandatory=False
2805 )
2833 )
2806 parts.append(part)
2834 parts.append(part)
2807
2835
2808 raise
2836 raise
2809 finally:
2837 finally:
2810 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
2838 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
2811 if recordout is not None:
2839 if recordout is not None:
2812 recordout(repo.ui.popbuffer())
2840 recordout(repo.ui.popbuffer())
2813 return r
2841 return r
2814
2842
2815
2843
2816 def _maybeapplyclonebundle(pullop):
2844 def _maybeapplyclonebundle(pullop):
2817 """Apply a clone bundle from a remote, if possible."""
2845 """Apply a clone bundle from a remote, if possible."""
2818
2846
2819 repo = pullop.repo
2847 repo = pullop.repo
2820 remote = pullop.remote
2848 remote = pullop.remote
2821
2849
2822 if not repo.ui.configbool(b'ui', b'clonebundles'):
2850 if not repo.ui.configbool(b'ui', b'clonebundles'):
2823 return
2851 return
2824
2852
2825 # Only run if local repo is empty.
2853 # Only run if local repo is empty.
2826 if len(repo):
2854 if len(repo):
2827 return
2855 return
2828
2856
2829 if pullop.heads:
2857 if pullop.heads:
2830 return
2858 return
2831
2859
2832 if not remote.capable(b'clonebundles'):
2860 if not remote.capable(b'clonebundles'):
2833 return
2861 return
2834
2862
2835 with remote.commandexecutor() as e:
2863 with remote.commandexecutor() as e:
2836 res = e.callcommand(b'clonebundles', {}).result()
2864 res = e.callcommand(b'clonebundles', {}).result()
2837
2865
2838 # If we call the wire protocol command, that's good enough to record the
2866 # If we call the wire protocol command, that's good enough to record the
2839 # attempt.
2867 # attempt.
2840 pullop.clonebundleattempted = True
2868 pullop.clonebundleattempted = True
2841
2869
2842 entries = parseclonebundlesmanifest(repo, res)
2870 entries = parseclonebundlesmanifest(repo, res)
2843 if not entries:
2871 if not entries:
2844 repo.ui.note(
2872 repo.ui.note(
2845 _(
2873 _(
2846 b'no clone bundles available on remote; '
2874 b'no clone bundles available on remote; '
2847 b'falling back to regular clone\n'
2875 b'falling back to regular clone\n'
2848 )
2876 )
2849 )
2877 )
2850 return
2878 return
2851
2879
2852 entries = filterclonebundleentries(
2880 entries = filterclonebundleentries(
2853 repo, entries, streamclonerequested=pullop.streamclonerequested
2881 repo, entries, streamclonerequested=pullop.streamclonerequested
2854 )
2882 )
2855
2883
2856 if not entries:
2884 if not entries:
2857 # There is a thundering herd concern here. However, if a server
2885 # There is a thundering herd concern here. However, if a server
2858 # operator doesn't advertise bundles appropriate for its clients,
2886 # operator doesn't advertise bundles appropriate for its clients,
2859 # they deserve what's coming. Furthermore, from a client's
2887 # they deserve what's coming. Furthermore, from a client's
2860 # perspective, no automatic fallback would mean not being able to
2888 # perspective, no automatic fallback would mean not being able to
2861 # clone!
2889 # clone!
2862 repo.ui.warn(
2890 repo.ui.warn(
2863 _(
2891 _(
2864 b'no compatible clone bundles available on server; '
2892 b'no compatible clone bundles available on server; '
2865 b'falling back to regular clone\n'
2893 b'falling back to regular clone\n'
2866 )
2894 )
2867 )
2895 )
2868 repo.ui.warn(
2896 repo.ui.warn(
2869 _(b'(you may want to report this to the server operator)\n')
2897 _(b'(you may want to report this to the server operator)\n')
2870 )
2898 )
2871 return
2899 return
2872
2900
2873 entries = sortclonebundleentries(repo.ui, entries)
2901 entries = sortclonebundleentries(repo.ui, entries)
2874
2902
2875 url = entries[0][b'URL']
2903 url = entries[0][b'URL']
2876 repo.ui.status(_(b'applying clone bundle from %s\n') % url)
2904 repo.ui.status(_(b'applying clone bundle from %s\n') % url)
2877 if trypullbundlefromurl(repo.ui, repo, url):
2905 if trypullbundlefromurl(repo.ui, repo, url):
2878 repo.ui.status(_(b'finished applying clone bundle\n'))
2906 repo.ui.status(_(b'finished applying clone bundle\n'))
2879 # Bundle failed.
2907 # Bundle failed.
2880 #
2908 #
2881 # We abort by default to avoid the thundering herd of
2909 # We abort by default to avoid the thundering herd of
2882 # clients flooding a server that was expecting expensive
2910 # clients flooding a server that was expecting expensive
2883 # clone load to be offloaded.
2911 # clone load to be offloaded.
2884 elif repo.ui.configbool(b'ui', b'clonebundlefallback'):
2912 elif repo.ui.configbool(b'ui', b'clonebundlefallback'):
2885 repo.ui.warn(_(b'falling back to normal clone\n'))
2913 repo.ui.warn(_(b'falling back to normal clone\n'))
2886 else:
2914 else:
2887 raise error.Abort(
2915 raise error.Abort(
2888 _(b'error applying bundle'),
2916 _(b'error applying bundle'),
2889 hint=_(
2917 hint=_(
2890 b'if this error persists, consider contacting '
2918 b'if this error persists, consider contacting '
2891 b'the server operator or disable clone '
2919 b'the server operator or disable clone '
2892 b'bundles via '
2920 b'bundles via '
2893 b'"--config ui.clonebundles=false"'
2921 b'"--config ui.clonebundles=false"'
2894 ),
2922 ),
2895 )
2923 )
2896
2924
2897
2925
2898 def parseclonebundlesmanifest(repo, s):
2926 def parseclonebundlesmanifest(repo, s):
2899 """Parses the raw text of a clone bundles manifest.
2927 """Parses the raw text of a clone bundles manifest.
2900
2928
2901 Returns a list of dicts. The dicts have a ``URL`` key corresponding
2929 Returns a list of dicts. The dicts have a ``URL`` key corresponding
2902 to the URL and other keys are the attributes for the entry.
2930 to the URL and other keys are the attributes for the entry.
2903 """
2931 """
2904 m = []
2932 m = []
2905 for line in s.splitlines():
2933 for line in s.splitlines():
2906 fields = line.split()
2934 fields = line.split()
2907 if not fields:
2935 if not fields:
2908 continue
2936 continue
2909 attrs = {b'URL': fields[0]}
2937 attrs = {b'URL': fields[0]}
2910 for rawattr in fields[1:]:
2938 for rawattr in fields[1:]:
2911 key, value = rawattr.split(b'=', 1)
2939 key, value = rawattr.split(b'=', 1)
2912 key = urlreq.unquote(key)
2940 key = urlreq.unquote(key)
2913 value = urlreq.unquote(value)
2941 value = urlreq.unquote(value)
2914 attrs[key] = value
2942 attrs[key] = value
2915
2943
2916 # Parse BUNDLESPEC into components. This makes client-side
2944 # Parse BUNDLESPEC into components. This makes client-side
2917 # preferences easier to specify since you can prefer a single
2945 # preferences easier to specify since you can prefer a single
2918 # component of the BUNDLESPEC.
2946 # component of the BUNDLESPEC.
2919 if key == b'BUNDLESPEC':
2947 if key == b'BUNDLESPEC':
2920 try:
2948 try:
2921 bundlespec = parsebundlespec(repo, value)
2949 bundlespec = parsebundlespec(repo, value)
2922 attrs[b'COMPRESSION'] = bundlespec.compression
2950 attrs[b'COMPRESSION'] = bundlespec.compression
2923 attrs[b'VERSION'] = bundlespec.version
2951 attrs[b'VERSION'] = bundlespec.version
2924 except error.InvalidBundleSpecification:
2952 except error.InvalidBundleSpecification:
2925 pass
2953 pass
2926 except error.UnsupportedBundleSpecification:
2954 except error.UnsupportedBundleSpecification:
2927 pass
2955 pass
2928
2956
2929 m.append(attrs)
2957 m.append(attrs)
2930
2958
2931 return m
2959 return m
2932
2960
2933
2961
2934 def isstreamclonespec(bundlespec):
2962 def isstreamclonespec(bundlespec):
2935 # Stream clone v1
2963 # Stream clone v1
2936 if bundlespec.wirecompression == b'UN' and bundlespec.wireversion == b's1':
2964 if bundlespec.wirecompression == b'UN' and bundlespec.wireversion == b's1':
2937 return True
2965 return True
2938
2966
2939 # Stream clone v2
2967 # Stream clone v2
2940 if (
2968 if (
2941 bundlespec.wirecompression == b'UN'
2969 bundlespec.wirecompression == b'UN'
2942 and bundlespec.wireversion == b'02'
2970 and bundlespec.wireversion == b'02'
2943 and bundlespec.contentopts.get(b'streamv2')
2971 and bundlespec.contentopts.get(b'streamv2')
2944 ):
2972 ):
2945 return True
2973 return True
2946
2974
2947 return False
2975 return False
2948
2976
2949
2977
2950 def filterclonebundleentries(repo, entries, streamclonerequested=False):
2978 def filterclonebundleentries(repo, entries, streamclonerequested=False):
2951 """Remove incompatible clone bundle manifest entries.
2979 """Remove incompatible clone bundle manifest entries.
2952
2980
2953 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
2981 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
2954 and returns a new list consisting of only the entries that this client
2982 and returns a new list consisting of only the entries that this client
2955 should be able to apply.
2983 should be able to apply.
2956
2984
2957 There is no guarantee we'll be able to apply all returned entries because
2985 There is no guarantee we'll be able to apply all returned entries because
2958 the metadata we use to filter on may be missing or wrong.
2986 the metadata we use to filter on may be missing or wrong.
2959 """
2987 """
2960 newentries = []
2988 newentries = []
2961 for entry in entries:
2989 for entry in entries:
2962 spec = entry.get(b'BUNDLESPEC')
2990 spec = entry.get(b'BUNDLESPEC')
2963 if spec:
2991 if spec:
2964 try:
2992 try:
2965 bundlespec = parsebundlespec(repo, spec, strict=True)
2993 bundlespec = parsebundlespec(repo, spec, strict=True)
2966
2994
2967 # If a stream clone was requested, filter out non-streamclone
2995 # If a stream clone was requested, filter out non-streamclone
2968 # entries.
2996 # entries.
2969 if streamclonerequested and not isstreamclonespec(bundlespec):
2997 if streamclonerequested and not isstreamclonespec(bundlespec):
2970 repo.ui.debug(
2998 repo.ui.debug(
2971 b'filtering %s because not a stream clone\n'
2999 b'filtering %s because not a stream clone\n'
2972 % entry[b'URL']
3000 % entry[b'URL']
2973 )
3001 )
2974 continue
3002 continue
2975
3003
2976 except error.InvalidBundleSpecification as e:
3004 except error.InvalidBundleSpecification as e:
2977 repo.ui.debug(stringutil.forcebytestr(e) + b'\n')
3005 repo.ui.debug(stringutil.forcebytestr(e) + b'\n')
2978 continue
3006 continue
2979 except error.UnsupportedBundleSpecification as e:
3007 except error.UnsupportedBundleSpecification as e:
2980 repo.ui.debug(
3008 repo.ui.debug(
2981 b'filtering %s because unsupported bundle '
3009 b'filtering %s because unsupported bundle '
2982 b'spec: %s\n' % (entry[b'URL'], stringutil.forcebytestr(e))
3010 b'spec: %s\n' % (entry[b'URL'], stringutil.forcebytestr(e))
2983 )
3011 )
2984 continue
3012 continue
2985 # If we don't have a spec and requested a stream clone, we don't know
3013 # If we don't have a spec and requested a stream clone, we don't know
2986 # what the entry is so don't attempt to apply it.
3014 # what the entry is so don't attempt to apply it.
2987 elif streamclonerequested:
3015 elif streamclonerequested:
2988 repo.ui.debug(
3016 repo.ui.debug(
2989 b'filtering %s because cannot determine if a stream '
3017 b'filtering %s because cannot determine if a stream '
2990 b'clone bundle\n' % entry[b'URL']
3018 b'clone bundle\n' % entry[b'URL']
2991 )
3019 )
2992 continue
3020 continue
2993
3021
2994 if b'REQUIRESNI' in entry and not sslutil.hassni:
3022 if b'REQUIRESNI' in entry and not sslutil.hassni:
2995 repo.ui.debug(
3023 repo.ui.debug(
2996 b'filtering %s because SNI not supported\n' % entry[b'URL']
3024 b'filtering %s because SNI not supported\n' % entry[b'URL']
2997 )
3025 )
2998 continue
3026 continue
2999
3027
3000 newentries.append(entry)
3028 newentries.append(entry)
3001
3029
3002 return newentries
3030 return newentries
3003
3031
3004
3032
3005 class clonebundleentry(object):
3033 class clonebundleentry(object):
3006 """Represents an item in a clone bundles manifest.
3034 """Represents an item in a clone bundles manifest.
3007
3035
3008 This rich class is needed to support sorting since sorted() in Python 3
3036 This rich class is needed to support sorting since sorted() in Python 3
3009 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
3037 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
3010 won't work.
3038 won't work.
3011 """
3039 """
3012
3040
3013 def __init__(self, value, prefers):
3041 def __init__(self, value, prefers):
3014 self.value = value
3042 self.value = value
3015 self.prefers = prefers
3043 self.prefers = prefers
3016
3044
3017 def _cmp(self, other):
3045 def _cmp(self, other):
3018 for prefkey, prefvalue in self.prefers:
3046 for prefkey, prefvalue in self.prefers:
3019 avalue = self.value.get(prefkey)
3047 avalue = self.value.get(prefkey)
3020 bvalue = other.value.get(prefkey)
3048 bvalue = other.value.get(prefkey)
3021
3049
3022 # Special case for b missing attribute and a matches exactly.
3050 # Special case for b missing attribute and a matches exactly.
3023 if avalue is not None and bvalue is None and avalue == prefvalue:
3051 if avalue is not None and bvalue is None and avalue == prefvalue:
3024 return -1
3052 return -1
3025
3053
3026 # Special case for a missing attribute and b matches exactly.
3054 # Special case for a missing attribute and b matches exactly.
3027 if bvalue is not None and avalue is None and bvalue == prefvalue:
3055 if bvalue is not None and avalue is None and bvalue == prefvalue:
3028 return 1
3056 return 1
3029
3057
3030 # We can't compare unless attribute present on both.
3058 # We can't compare unless attribute present on both.
3031 if avalue is None or bvalue is None:
3059 if avalue is None or bvalue is None:
3032 continue
3060 continue
3033
3061
3034 # Same values should fall back to next attribute.
3062 # Same values should fall back to next attribute.
3035 if avalue == bvalue:
3063 if avalue == bvalue:
3036 continue
3064 continue
3037
3065
3038 # Exact matches come first.
3066 # Exact matches come first.
3039 if avalue == prefvalue:
3067 if avalue == prefvalue:
3040 return -1
3068 return -1
3041 if bvalue == prefvalue:
3069 if bvalue == prefvalue:
3042 return 1
3070 return 1
3043
3071
3044 # Fall back to next attribute.
3072 # Fall back to next attribute.
3045 continue
3073 continue
3046
3074
3047 # If we got here we couldn't sort by attributes and prefers. Fall
3075 # If we got here we couldn't sort by attributes and prefers. Fall
3048 # back to index order.
3076 # back to index order.
3049 return 0
3077 return 0
3050
3078
3051 def __lt__(self, other):
3079 def __lt__(self, other):
3052 return self._cmp(other) < 0
3080 return self._cmp(other) < 0
3053
3081
3054 def __gt__(self, other):
3082 def __gt__(self, other):
3055 return self._cmp(other) > 0
3083 return self._cmp(other) > 0
3056
3084
3057 def __eq__(self, other):
3085 def __eq__(self, other):
3058 return self._cmp(other) == 0
3086 return self._cmp(other) == 0
3059
3087
3060 def __le__(self, other):
3088 def __le__(self, other):
3061 return self._cmp(other) <= 0
3089 return self._cmp(other) <= 0
3062
3090
3063 def __ge__(self, other):
3091 def __ge__(self, other):
3064 return self._cmp(other) >= 0
3092 return self._cmp(other) >= 0
3065
3093
3066 def __ne__(self, other):
3094 def __ne__(self, other):
3067 return self._cmp(other) != 0
3095 return self._cmp(other) != 0
3068
3096
3069
3097
3070 def sortclonebundleentries(ui, entries):
3098 def sortclonebundleentries(ui, entries):
3071 prefers = ui.configlist(b'ui', b'clonebundleprefers')
3099 prefers = ui.configlist(b'ui', b'clonebundleprefers')
3072 if not prefers:
3100 if not prefers:
3073 return list(entries)
3101 return list(entries)
3074
3102
3075 def _split(p):
3103 def _split(p):
3076 if b'=' not in p:
3104 if b'=' not in p:
3077 hint = _(b"each comma separated item should be key=value pairs")
3105 hint = _(b"each comma separated item should be key=value pairs")
3078 raise error.Abort(
3106 raise error.Abort(
3079 _(b"invalid ui.clonebundleprefers item: %s") % p, hint=hint
3107 _(b"invalid ui.clonebundleprefers item: %s") % p, hint=hint
3080 )
3108 )
3081 return p.split(b'=', 1)
3109 return p.split(b'=', 1)
3082
3110
3083 prefers = [_split(p) for p in prefers]
3111 prefers = [_split(p) for p in prefers]
3084
3112
3085 items = sorted(clonebundleentry(v, prefers) for v in entries)
3113 items = sorted(clonebundleentry(v, prefers) for v in entries)
3086 return [i.value for i in items]
3114 return [i.value for i in items]
3087
3115
3088
3116
3089 def trypullbundlefromurl(ui, repo, url):
3117 def trypullbundlefromurl(ui, repo, url):
3090 """Attempt to apply a bundle from a URL."""
3118 """Attempt to apply a bundle from a URL."""
3091 with repo.lock(), repo.transaction(b'bundleurl') as tr:
3119 with repo.lock(), repo.transaction(b'bundleurl') as tr:
3092 try:
3120 try:
3093 fh = urlmod.open(ui, url)
3121 fh = urlmod.open(ui, url)
3094 cg = readbundle(ui, fh, b'stream')
3122 cg = readbundle(ui, fh, b'stream')
3095
3123
3096 if isinstance(cg, streamclone.streamcloneapplier):
3124 if isinstance(cg, streamclone.streamcloneapplier):
3097 cg.apply(repo)
3125 cg.apply(repo)
3098 else:
3126 else:
3099 bundle2.applybundle(repo, cg, tr, b'clonebundles', url)
3127 bundle2.applybundle(repo, cg, tr, b'clonebundles', url)
3100 return True
3128 return True
3101 except urlerr.httperror as e:
3129 except urlerr.httperror as e:
3102 ui.warn(
3130 ui.warn(
3103 _(b'HTTP error fetching bundle: %s\n')
3131 _(b'HTTP error fetching bundle: %s\n')
3104 % stringutil.forcebytestr(e)
3132 % stringutil.forcebytestr(e)
3105 )
3133 )
3106 except urlerr.urlerror as e:
3134 except urlerr.urlerror as e:
3107 ui.warn(
3135 ui.warn(
3108 _(b'error fetching bundle: %s\n')
3136 _(b'error fetching bundle: %s\n')
3109 % stringutil.forcebytestr(e.reason)
3137 % stringutil.forcebytestr(e.reason)
3110 )
3138 )
3111
3139
3112 return False
3140 return False
@@ -1,83 +1,86 b''
1 == New Features ==
1 == New Features ==
2
2
3 * `hg purge`/`hg clean` can now delete ignored files instead of
3 * `hg purge`/`hg clean` can now delete ignored files instead of
4 untracked files, with the new -i flag.
4 untracked files, with the new -i flag.
5
5
6 * `hg pull` now has a `--confirm` flag to prompt before applying changes.
7 Config option `pull.confirm` is also added for that.
8
6 * `hg log` now defaults to using an '%' symbol for commits involved
9 * `hg log` now defaults to using an '%' symbol for commits involved
7 in unresolved merge conflicts. That includes unresolved conflicts
10 in unresolved merge conflicts. That includes unresolved conflicts
8 caused by e.g. `hg update --merge` and `hg graft`. '@' still takes
11 caused by e.g. `hg update --merge` and `hg graft`. '@' still takes
9 precedence, so what used to be marked '@' still is.
12 precedence, so what used to be marked '@' still is.
10
13
11 * New `conflictlocal()` and `conflictother()` revsets return the
14 * New `conflictlocal()` and `conflictother()` revsets return the
12 commits that are being merged, when there are conflicts. Also works
15 commits that are being merged, when there are conflicts. Also works
13 for conflicts caused by e.g. `hg graft`.
16 for conflicts caused by e.g. `hg graft`.
14
17
15 * `hg copy --forget` can be used to unmark a file as copied.
18 * `hg copy --forget` can be used to unmark a file as copied.
16
19
17 * The `format.revlog-compression` configuration entry now accept a list. The
20 * The `format.revlog-compression` configuration entry now accept a list. The
18 first available option will be used. for example setting::
21 first available option will be used. for example setting::
19
22
20 [format]
23 [format]
21 revlog-compression=zstd, zlib
24 revlog-compression=zstd, zlib
22
25
23 Will use `zstd` compression for new repositories is available, and will
26 Will use `zstd` compression for new repositories is available, and will
24 simply fall back to `zlib` if not.
27 simply fall back to `zlib` if not.
25
28
26 * `hg debugmergestate` output is now templated, which may be useful
29 * `hg debugmergestate` output is now templated, which may be useful
27 e.g. for IDEs that want to help the user resolve merge conflicts.
30 e.g. for IDEs that want to help the user resolve merge conflicts.
28
31
29
32
30 == New Experimental Features ==
33 == New Experimental Features ==
31
34
32 * `hg copy` now supports a `--at-rev` argument to mark files as
35 * `hg copy` now supports a `--at-rev` argument to mark files as
33 copied in the specified commit. It only works with `--after` for
36 copied in the specified commit. It only works with `--after` for
34 now (i.e., it's only useful for marking files copied using non-hg
37 now (i.e., it's only useful for marking files copied using non-hg
35 `cp` as copied).
38 `cp` as copied).
36
39
37 * Use `hg copy --forget --at-rev REV` to unmark already committed
40 * Use `hg copy --forget --at-rev REV` to unmark already committed
38 copies.
41 copies.
39
42
40 == Bug Fixes ==
43 == Bug Fixes ==
41
44
42 * Fix server exception when concurrent pushes delete the same bookmark
45 * Fix server exception when concurrent pushes delete the same bookmark
43
46
44 * Prevent pushes of divergent bookmarks (foo@remote)
47 * Prevent pushes of divergent bookmarks (foo@remote)
45
48
46 * The push error "remote repository changed while pushing - please
49 * The push error "remote repository changed while pushing - please
47 try again" now only happens when a concurrent push changed related
50 try again" now only happens when a concurrent push changed related
48 heads (instead of when a concurrent pushed any revision).
51 heads (instead of when a concurrent pushed any revision).
49
52
50
53
51 == Backwards Compatibility Changes ==
54 == Backwards Compatibility Changes ==
52
55
53 * When `hg rebase` pauses for merge conflict resolution, the working
56 * When `hg rebase` pauses for merge conflict resolution, the working
54 copy will no longer have the rebased node as a second parent. You
57 copy will no longer have the rebased node as a second parent. You
55 can use the new `conflictparents()` revset for finding the other
58 can use the new `conflictparents()` revset for finding the other
56 parent during a conflict.
59 parent during a conflict.
57
60
58 * `hg recover` does not verify the validity of the whole repository
61 * `hg recover` does not verify the validity of the whole repository
59 anymore. You can pass `--verify` or call `hg verify` if necessary.
62 anymore. You can pass `--verify` or call `hg verify` if necessary.
60
63
61 * `hg debugmergestate` output format changed. Let us know if that is
64 * `hg debugmergestate` output format changed. Let us know if that is
62 causing you problems and we'll roll it back.
65 causing you problems and we'll roll it back.
63
66
64 * Resolved merge conflicts are now cleared by `hg commit` even if the
67 * Resolved merge conflicts are now cleared by `hg commit` even if the
65 working copy has no changes.
68 working copy has no changes.
66
69
67
70
68 == Internal API Changes ==
71 == Internal API Changes ==
69
72
70 * The deprecated `ui.progress()` has now been deleted. Please use
73 * The deprecated `ui.progress()` has now been deleted. Please use
71 `ui.makeprogress()` instead.
74 `ui.makeprogress()` instead.
72
75
73 * `hg.merge()` now takes a `ctx` instead of the previous `repo` and
76 * `hg.merge()` now takes a `ctx` instead of the previous `repo` and
74 `node` arguments.
77 `node` arguments.
75
78
76 * `hg.merge()` has lost its `abort` argument. Please call
79 * `hg.merge()` has lost its `abort` argument. Please call
77 `hg.abortmerge()` directly instead.
80 `hg.abortmerge()` directly instead.
78
81
79 * `hg.merge()` has lost its `mergeforce` argument. It should have
82 * `hg.merge()` has lost its `mergeforce` argument. It should have
80 only ever been called with the same value as the `force` argument.
83 only ever been called with the same value as the `force` argument.
81
84
82 * The `*others` argument of `cmdutil.check_incompatible_arguments()`
85 * The `*others` argument of `cmdutil.check_incompatible_arguments()`
83 changed from being varargs argument to being a single collection.
86 changed from being varargs argument to being a single collection.
@@ -1,431 +1,431 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 abort
3 abort
4 add
4 add
5 addremove
5 addremove
6 annotate
6 annotate
7 archive
7 archive
8 backout
8 backout
9 bisect
9 bisect
10 bookmarks
10 bookmarks
11 branch
11 branch
12 branches
12 branches
13 bundle
13 bundle
14 cat
14 cat
15 clone
15 clone
16 commit
16 commit
17 config
17 config
18 continue
18 continue
19 copy
19 copy
20 diff
20 diff
21 export
21 export
22 files
22 files
23 forget
23 forget
24 graft
24 graft
25 grep
25 grep
26 heads
26 heads
27 help
27 help
28 identify
28 identify
29 import
29 import
30 incoming
30 incoming
31 init
31 init
32 locate
32 locate
33 log
33 log
34 manifest
34 manifest
35 merge
35 merge
36 outgoing
36 outgoing
37 parents
37 parents
38 paths
38 paths
39 phase
39 phase
40 pull
40 pull
41 push
41 push
42 recover
42 recover
43 remove
43 remove
44 rename
44 rename
45 resolve
45 resolve
46 revert
46 revert
47 rollback
47 rollback
48 root
48 root
49 serve
49 serve
50 shelve
50 shelve
51 status
51 status
52 summary
52 summary
53 tag
53 tag
54 tags
54 tags
55 tip
55 tip
56 unbundle
56 unbundle
57 unshelve
57 unshelve
58 update
58 update
59 verify
59 verify
60 version
60 version
61
61
62 Show all commands that start with "a"
62 Show all commands that start with "a"
63 $ hg debugcomplete a
63 $ hg debugcomplete a
64 abort
64 abort
65 add
65 add
66 addremove
66 addremove
67 annotate
67 annotate
68 archive
68 archive
69
69
70 Do not show debug commands if there are other candidates
70 Do not show debug commands if there are other candidates
71 $ hg debugcomplete d
71 $ hg debugcomplete d
72 diff
72 diff
73
73
74 Show debug commands if there are no other candidates
74 Show debug commands if there are no other candidates
75 $ hg debugcomplete debug
75 $ hg debugcomplete debug
76 debugancestor
76 debugancestor
77 debugapplystreamclonebundle
77 debugapplystreamclonebundle
78 debugbackupbundle
78 debugbackupbundle
79 debugbuilddag
79 debugbuilddag
80 debugbundle
80 debugbundle
81 debugcapabilities
81 debugcapabilities
82 debugcheckstate
82 debugcheckstate
83 debugcolor
83 debugcolor
84 debugcommands
84 debugcommands
85 debugcomplete
85 debugcomplete
86 debugconfig
86 debugconfig
87 debugcreatestreamclonebundle
87 debugcreatestreamclonebundle
88 debugdag
88 debugdag
89 debugdata
89 debugdata
90 debugdate
90 debugdate
91 debugdeltachain
91 debugdeltachain
92 debugdirstate
92 debugdirstate
93 debugdiscovery
93 debugdiscovery
94 debugdownload
94 debugdownload
95 debugextensions
95 debugextensions
96 debugfileset
96 debugfileset
97 debugformat
97 debugformat
98 debugfsinfo
98 debugfsinfo
99 debuggetbundle
99 debuggetbundle
100 debugignore
100 debugignore
101 debugindex
101 debugindex
102 debugindexdot
102 debugindexdot
103 debugindexstats
103 debugindexstats
104 debuginstall
104 debuginstall
105 debugknown
105 debugknown
106 debuglabelcomplete
106 debuglabelcomplete
107 debuglocks
107 debuglocks
108 debugmanifestfulltextcache
108 debugmanifestfulltextcache
109 debugmergestate
109 debugmergestate
110 debugnamecomplete
110 debugnamecomplete
111 debugnodemap
111 debugnodemap
112 debugobsolete
112 debugobsolete
113 debugp1copies
113 debugp1copies
114 debugp2copies
114 debugp2copies
115 debugpathcomplete
115 debugpathcomplete
116 debugpathcopies
116 debugpathcopies
117 debugpeer
117 debugpeer
118 debugpickmergetool
118 debugpickmergetool
119 debugpushkey
119 debugpushkey
120 debugpvec
120 debugpvec
121 debugrebuilddirstate
121 debugrebuilddirstate
122 debugrebuildfncache
122 debugrebuildfncache
123 debugrename
123 debugrename
124 debugrevlog
124 debugrevlog
125 debugrevlogindex
125 debugrevlogindex
126 debugrevspec
126 debugrevspec
127 debugserve
127 debugserve
128 debugsetparents
128 debugsetparents
129 debugsidedata
129 debugsidedata
130 debugssl
130 debugssl
131 debugsub
131 debugsub
132 debugsuccessorssets
132 debugsuccessorssets
133 debugtagscache
133 debugtagscache
134 debugtemplate
134 debugtemplate
135 debuguigetpass
135 debuguigetpass
136 debuguiprompt
136 debuguiprompt
137 debugupdatecaches
137 debugupdatecaches
138 debugupgraderepo
138 debugupgraderepo
139 debugwalk
139 debugwalk
140 debugwhyunstable
140 debugwhyunstable
141 debugwireargs
141 debugwireargs
142 debugwireproto
142 debugwireproto
143
143
144 Do not show the alias of a debug command if there are other candidates
144 Do not show the alias of a debug command if there are other candidates
145 (this should hide rawcommit)
145 (this should hide rawcommit)
146 $ hg debugcomplete r
146 $ hg debugcomplete r
147 recover
147 recover
148 remove
148 remove
149 rename
149 rename
150 resolve
150 resolve
151 revert
151 revert
152 rollback
152 rollback
153 root
153 root
154 Show the alias of a debug command if there are no other candidates
154 Show the alias of a debug command if there are no other candidates
155 $ hg debugcomplete rawc
155 $ hg debugcomplete rawc
156
156
157
157
158 Show the global options
158 Show the global options
159 $ hg debugcomplete --options | sort
159 $ hg debugcomplete --options | sort
160 --color
160 --color
161 --config
161 --config
162 --cwd
162 --cwd
163 --debug
163 --debug
164 --debugger
164 --debugger
165 --encoding
165 --encoding
166 --encodingmode
166 --encodingmode
167 --help
167 --help
168 --hidden
168 --hidden
169 --noninteractive
169 --noninteractive
170 --pager
170 --pager
171 --profile
171 --profile
172 --quiet
172 --quiet
173 --repository
173 --repository
174 --time
174 --time
175 --traceback
175 --traceback
176 --verbose
176 --verbose
177 --version
177 --version
178 -R
178 -R
179 -h
179 -h
180 -q
180 -q
181 -v
181 -v
182 -y
182 -y
183
183
184 Show the options for the "serve" command
184 Show the options for the "serve" command
185 $ hg debugcomplete --options serve | sort
185 $ hg debugcomplete --options serve | sort
186 --accesslog
186 --accesslog
187 --address
187 --address
188 --certificate
188 --certificate
189 --cmdserver
189 --cmdserver
190 --color
190 --color
191 --config
191 --config
192 --cwd
192 --cwd
193 --daemon
193 --daemon
194 --daemon-postexec
194 --daemon-postexec
195 --debug
195 --debug
196 --debugger
196 --debugger
197 --encoding
197 --encoding
198 --encodingmode
198 --encodingmode
199 --errorlog
199 --errorlog
200 --help
200 --help
201 --hidden
201 --hidden
202 --ipv6
202 --ipv6
203 --name
203 --name
204 --noninteractive
204 --noninteractive
205 --pager
205 --pager
206 --pid-file
206 --pid-file
207 --port
207 --port
208 --prefix
208 --prefix
209 --print-url
209 --print-url
210 --profile
210 --profile
211 --quiet
211 --quiet
212 --repository
212 --repository
213 --stdio
213 --stdio
214 --style
214 --style
215 --subrepos
215 --subrepos
216 --templates
216 --templates
217 --time
217 --time
218 --traceback
218 --traceback
219 --verbose
219 --verbose
220 --version
220 --version
221 --web-conf
221 --web-conf
222 -6
222 -6
223 -A
223 -A
224 -E
224 -E
225 -R
225 -R
226 -S
226 -S
227 -a
227 -a
228 -d
228 -d
229 -h
229 -h
230 -n
230 -n
231 -p
231 -p
232 -q
232 -q
233 -t
233 -t
234 -v
234 -v
235 -y
235 -y
236
236
237 Show an error if we use --options with an ambiguous abbreviation
237 Show an error if we use --options with an ambiguous abbreviation
238 $ hg debugcomplete --options s
238 $ hg debugcomplete --options s
239 hg: command 's' is ambiguous:
239 hg: command 's' is ambiguous:
240 serve shelve showconfig status summary
240 serve shelve showconfig status summary
241 [255]
241 [255]
242
242
243 Show all commands + options
243 Show all commands + options
244 $ hg debugcommands
244 $ hg debugcommands
245 abort: dry-run
245 abort: dry-run
246 add: include, exclude, subrepos, dry-run
246 add: include, exclude, subrepos, dry-run
247 addremove: similarity, subrepos, include, exclude, dry-run
247 addremove: similarity, subrepos, include, exclude, dry-run
248 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
248 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
249 archive: no-decode, prefix, rev, type, subrepos, include, exclude
249 archive: no-decode, prefix, rev, type, subrepos, include, exclude
250 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
250 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
251 bisect: reset, good, bad, skip, extend, command, noupdate
251 bisect: reset, good, bad, skip, extend, command, noupdate
252 bookmarks: force, rev, delete, rename, inactive, list, template
252 bookmarks: force, rev, delete, rename, inactive, list, template
253 branch: force, clean, rev
253 branch: force, clean, rev
254 branches: active, closed, rev, template
254 branches: active, closed, rev, template
255 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
255 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
256 cat: output, rev, decode, include, exclude, template
256 cat: output, rev, decode, include, exclude, template
257 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
257 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
258 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
258 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
259 config: untrusted, edit, local, global, template
259 config: untrusted, edit, local, global, template
260 continue: dry-run
260 continue: dry-run
261 copy: forget, after, at-rev, force, include, exclude, dry-run
261 copy: forget, after, at-rev, force, include, exclude, dry-run
262 debugancestor:
262 debugancestor:
263 debugapplystreamclonebundle:
263 debugapplystreamclonebundle:
264 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
264 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
265 debugbuilddag: mergeable-file, overwritten-file, new-file
265 debugbuilddag: mergeable-file, overwritten-file, new-file
266 debugbundle: all, part-type, spec
266 debugbundle: all, part-type, spec
267 debugcapabilities:
267 debugcapabilities:
268 debugcheckstate:
268 debugcheckstate:
269 debugcolor: style
269 debugcolor: style
270 debugcommands:
270 debugcommands:
271 debugcomplete: options
271 debugcomplete: options
272 debugcreatestreamclonebundle:
272 debugcreatestreamclonebundle:
273 debugdag: tags, branches, dots, spaces
273 debugdag: tags, branches, dots, spaces
274 debugdata: changelog, manifest, dir
274 debugdata: changelog, manifest, dir
275 debugdate: extended
275 debugdate: extended
276 debugdeltachain: changelog, manifest, dir, template
276 debugdeltachain: changelog, manifest, dir, template
277 debugdirstate: nodates, dates, datesort
277 debugdirstate: nodates, dates, datesort
278 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
278 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
279 debugdownload: output
279 debugdownload: output
280 debugextensions: template
280 debugextensions: template
281 debugfileset: rev, all-files, show-matcher, show-stage
281 debugfileset: rev, all-files, show-matcher, show-stage
282 debugformat: template
282 debugformat: template
283 debugfsinfo:
283 debugfsinfo:
284 debuggetbundle: head, common, type
284 debuggetbundle: head, common, type
285 debugignore:
285 debugignore:
286 debugindex: changelog, manifest, dir, template
286 debugindex: changelog, manifest, dir, template
287 debugindexdot: changelog, manifest, dir
287 debugindexdot: changelog, manifest, dir
288 debugindexstats:
288 debugindexstats:
289 debuginstall: template
289 debuginstall: template
290 debugknown:
290 debugknown:
291 debuglabelcomplete:
291 debuglabelcomplete:
292 debuglocks: force-lock, force-wlock, set-lock, set-wlock
292 debuglocks: force-lock, force-wlock, set-lock, set-wlock
293 debugmanifestfulltextcache: clear, add
293 debugmanifestfulltextcache: clear, add
294 debugmergestate: style, template
294 debugmergestate: style, template
295 debugnamecomplete:
295 debugnamecomplete:
296 debugnodemap: dump-new, dump-disk, check, metadata
296 debugnodemap: dump-new, dump-disk, check, metadata
297 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
297 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
298 debugp1copies: rev
298 debugp1copies: rev
299 debugp2copies: rev
299 debugp2copies: rev
300 debugpathcomplete: full, normal, added, removed
300 debugpathcomplete: full, normal, added, removed
301 debugpathcopies: include, exclude
301 debugpathcopies: include, exclude
302 debugpeer:
302 debugpeer:
303 debugpickmergetool: rev, changedelete, include, exclude, tool
303 debugpickmergetool: rev, changedelete, include, exclude, tool
304 debugpushkey:
304 debugpushkey:
305 debugpvec:
305 debugpvec:
306 debugrebuilddirstate: rev, minimal
306 debugrebuilddirstate: rev, minimal
307 debugrebuildfncache:
307 debugrebuildfncache:
308 debugrename: rev
308 debugrename: rev
309 debugrevlog: changelog, manifest, dir, dump
309 debugrevlog: changelog, manifest, dir, dump
310 debugrevlogindex: changelog, manifest, dir, format
310 debugrevlogindex: changelog, manifest, dir, format
311 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
311 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
312 debugserve: sshstdio, logiofd, logiofile
312 debugserve: sshstdio, logiofd, logiofile
313 debugsetparents:
313 debugsetparents:
314 debugsidedata: changelog, manifest, dir
314 debugsidedata: changelog, manifest, dir
315 debugssl:
315 debugssl:
316 debugsub: rev
316 debugsub: rev
317 debugsuccessorssets: closest
317 debugsuccessorssets: closest
318 debugtagscache:
318 debugtagscache:
319 debugtemplate: rev, define
319 debugtemplate: rev, define
320 debuguigetpass: prompt
320 debuguigetpass: prompt
321 debuguiprompt: prompt
321 debuguiprompt: prompt
322 debugupdatecaches:
322 debugupdatecaches:
323 debugupgraderepo: optimize, run, backup, changelog, manifest
323 debugupgraderepo: optimize, run, backup, changelog, manifest
324 debugwalk: include, exclude
324 debugwalk: include, exclude
325 debugwhyunstable:
325 debugwhyunstable:
326 debugwireargs: three, four, five, ssh, remotecmd, insecure
326 debugwireargs: three, four, five, ssh, remotecmd, insecure
327 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
327 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
328 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
328 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
329 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
329 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
330 files: rev, print0, include, exclude, template, subrepos
330 files: rev, print0, include, exclude, template, subrepos
331 forget: interactive, include, exclude, dry-run
331 forget: interactive, include, exclude, dry-run
332 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
332 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
333 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
333 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
334 heads: rev, topo, active, closed, style, template
334 heads: rev, topo, active, closed, style, template
335 help: extension, command, keyword, system
335 help: extension, command, keyword, system
336 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
336 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
337 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
337 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
338 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
338 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
339 init: ssh, remotecmd, insecure
339 init: ssh, remotecmd, insecure
340 locate: rev, print0, fullpath, include, exclude
340 locate: rev, print0, fullpath, include, exclude
341 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
341 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
342 manifest: rev, all, template
342 manifest: rev, all, template
343 merge: force, rev, preview, abort, tool
343 merge: force, rev, preview, abort, tool
344 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
344 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
345 parents: rev, style, template
345 parents: rev, style, template
346 paths: template
346 paths: template
347 phase: public, draft, secret, force, rev
347 phase: public, draft, secret, force, rev
348 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
348 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
349 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
349 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
350 recover: verify
350 recover: verify
351 remove: after, force, subrepos, include, exclude, dry-run
351 remove: after, force, subrepos, include, exclude, dry-run
352 rename: after, force, include, exclude, dry-run
352 rename: after, force, include, exclude, dry-run
353 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
353 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
354 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
354 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
355 rollback: dry-run, force
355 rollback: dry-run, force
356 root: template
356 root: template
357 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
357 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
358 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
358 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
359 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
359 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
360 summary: remote
360 summary: remote
361 tag: force, local, rev, remove, edit, message, date, user
361 tag: force, local, rev, remove, edit, message, date, user
362 tags: template
362 tags: template
363 tip: patch, git, style, template
363 tip: patch, git, style, template
364 unbundle: update
364 unbundle: update
365 unshelve: abort, continue, interactive, keep, name, tool, date
365 unshelve: abort, continue, interactive, keep, name, tool, date
366 update: clean, check, merge, date, rev, tool
366 update: clean, check, merge, date, rev, tool
367 verify: full
367 verify: full
368 version: template
368 version: template
369
369
370 $ hg init a
370 $ hg init a
371 $ cd a
371 $ cd a
372 $ echo fee > fee
372 $ echo fee > fee
373 $ hg ci -q -Amfee
373 $ hg ci -q -Amfee
374 $ hg tag fee
374 $ hg tag fee
375 $ mkdir fie
375 $ mkdir fie
376 $ echo dead > fie/dead
376 $ echo dead > fie/dead
377 $ echo live > fie/live
377 $ echo live > fie/live
378 $ hg bookmark fo
378 $ hg bookmark fo
379 $ hg branch -q fie
379 $ hg branch -q fie
380 $ hg ci -q -Amfie
380 $ hg ci -q -Amfie
381 $ echo fo > fo
381 $ echo fo > fo
382 $ hg branch -qf default
382 $ hg branch -qf default
383 $ hg ci -q -Amfo
383 $ hg ci -q -Amfo
384 $ echo Fum > Fum
384 $ echo Fum > Fum
385 $ hg ci -q -AmFum
385 $ hg ci -q -AmFum
386 $ hg bookmark Fum
386 $ hg bookmark Fum
387
387
388 Test debugpathcomplete
388 Test debugpathcomplete
389
389
390 $ hg debugpathcomplete f
390 $ hg debugpathcomplete f
391 fee
391 fee
392 fie
392 fie
393 fo
393 fo
394 $ hg debugpathcomplete -f f
394 $ hg debugpathcomplete -f f
395 fee
395 fee
396 fie/dead
396 fie/dead
397 fie/live
397 fie/live
398 fo
398 fo
399
399
400 $ hg rm Fum
400 $ hg rm Fum
401 $ hg debugpathcomplete -r F
401 $ hg debugpathcomplete -r F
402 Fum
402 Fum
403
403
404 Test debugnamecomplete
404 Test debugnamecomplete
405
405
406 $ hg debugnamecomplete
406 $ hg debugnamecomplete
407 Fum
407 Fum
408 default
408 default
409 fee
409 fee
410 fie
410 fie
411 fo
411 fo
412 tip
412 tip
413 $ hg debugnamecomplete f
413 $ hg debugnamecomplete f
414 fee
414 fee
415 fie
415 fie
416 fo
416 fo
417
417
418 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
418 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
419 used for completions in some shells.
419 used for completions in some shells.
420
420
421 $ hg debuglabelcomplete
421 $ hg debuglabelcomplete
422 Fum
422 Fum
423 default
423 default
424 fee
424 fee
425 fie
425 fie
426 fo
426 fo
427 tip
427 tip
428 $ hg debuglabelcomplete f
428 $ hg debuglabelcomplete f
429 fee
429 fee
430 fie
430 fie
431 fo
431 fo
@@ -1,578 +1,603 b''
1 =============================
1 =============================
2 Test distributed obsolescence
2 Test distributed obsolescence
3 =============================
3 =============================
4
4
5 This file test various cases where data (changeset, phase, obsmarkers) is
5 This file test various cases where data (changeset, phase, obsmarkers) is
6 added to the repository in a specific order. Usually, this order is unlikely
6 added to the repository in a specific order. Usually, this order is unlikely
7 to happen in the local case but can easily happen in the distributed case.
7 to happen in the local case but can easily happen in the distributed case.
8
8
9 $ unset HGUSER
9 $ unset HGUSER
10 $ unset EMAIL
10 $ unset EMAIL
11 $ . $TESTDIR/testlib/obsmarker-common.sh
11 $ . $TESTDIR/testlib/obsmarker-common.sh
12 $ cat >> $HGRCPATH << EOF
12 $ cat >> $HGRCPATH << EOF
13 > [extensions]
13 > [extensions]
14 > rebase =
14 > rebase =
15 > [experimental]
15 > [experimental]
16 > evolution = all
16 > evolution = all
17 > [phases]
17 > [phases]
18 > publish = False
18 > publish = False
19 > [ui]
19 > [ui]
20 > logtemplate= {rev}:{node|short} {desc}{if(obsfate, " [{join(obsfate, "; ")}]")}\n
20 > logtemplate= {rev}:{node|short} {desc}{if(obsfate, " [{join(obsfate, "; ")}]")}\n
21 > EOF
21 > EOF
22
22
23 Check distributed chain building
23 Check distributed chain building
24 ================================
24 ================================
25
25
26 Test case where a changeset is marked as a successor of another local
26 Test case where a changeset is marked as a successor of another local
27 changeset while the successor has already been obsoleted remotely.
27 changeset while the successor has already been obsoleted remotely.
28
28
29 The chain of evolution should seamlessly connect and all but the new version
29 The chain of evolution should seamlessly connect and all but the new version
30 (created remotely) should be seen as obsolete.
30 (created remotely) should be seen as obsolete.
31
31
32 Initial setup
32 Initial setup
33
33
34 $ mkdir distributed-chain-building
34 $ mkdir distributed-chain-building
35 $ cd distributed-chain-building
35 $ cd distributed-chain-building
36 $ hg init server
36 $ hg init server
37 $ cd server
37 $ cd server
38 $ cat << EOF >> .hg/hgrc
38 $ cat << EOF >> .hg/hgrc
39 > [ui]
39 > [ui]
40 > username = server
40 > username = server
41 > EOF
41 > EOF
42 $ mkcommit ROOT
42 $ mkcommit ROOT
43 $ mkcommit c_A0
43 $ mkcommit c_A0
44 $ hg up 'desc("ROOT")'
44 $ hg up 'desc("ROOT")'
45 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
45 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
46 $ mkcommit c_A1
46 $ mkcommit c_A1
47 created new head
47 created new head
48 $ hg up 'desc("ROOT")'
48 $ hg up 'desc("ROOT")'
49 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
49 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
50 $ mkcommit c_B0
50 $ mkcommit c_B0
51 created new head
51 created new head
52 $ hg debugobsolete `getid 'desc("c_A0")'` `getid 'desc("c_A1")'`
52 $ hg debugobsolete `getid 'desc("c_A0")'` `getid 'desc("c_A1")'`
53 1 new obsolescence markers
53 1 new obsolescence markers
54 obsoleted 1 changesets
54 obsoleted 1 changesets
55 $ hg log -G --hidden -v
55 $ hg log -G --hidden -v
56 @ 3:e5d7dda7cd28 c_B0
56 @ 3:e5d7dda7cd28 c_B0
57 |
57 |
58 | o 2:7f6b0a6f5c25 c_A1
58 | o 2:7f6b0a6f5c25 c_A1
59 |/
59 |/
60 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
60 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
61 |/
61 |/
62 o 0:e82fb8d02bbf ROOT
62 o 0:e82fb8d02bbf ROOT
63
63
64 $ hg debugobsolete
64 $ hg debugobsolete
65 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
65 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
66 $ cd ..
66 $ cd ..
67
67
68 duplicate the repo for the client:
68 duplicate the repo for the client:
69
69
70 $ cp -R server client
70 $ cp -R server client
71 $ cat << EOF >> client/.hg/hgrc
71 $ cat << EOF >> client/.hg/hgrc
72 > [paths]
72 > [paths]
73 > default = ../server/
73 > default = ../server/
74 > [ui]
74 > [ui]
75 > username = client
75 > username = client
76 > EOF
76 > EOF
77
77
78 server side: create new revision on the server (obsoleting another one)
78 server side: create new revision on the server (obsoleting another one)
79
79
80 $ cd server
80 $ cd server
81 $ hg up 'desc("ROOT")'
81 $ hg up 'desc("ROOT")'
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 $ mkcommit c_B1
83 $ mkcommit c_B1
84 created new head
84 created new head
85 $ hg debugobsolete `getid 'desc("c_B0")'` `getid 'desc("c_B1")'`
85 $ hg debugobsolete `getid 'desc("c_B0")'` `getid 'desc("c_B1")'`
86 1 new obsolescence markers
86 1 new obsolescence markers
87 obsoleted 1 changesets
87 obsoleted 1 changesets
88 $ hg log -G
88 $ hg log -G
89 @ 4:391a2bf12b1b c_B1
89 @ 4:391a2bf12b1b c_B1
90 |
90 |
91 | o 2:7f6b0a6f5c25 c_A1
91 | o 2:7f6b0a6f5c25 c_A1
92 |/
92 |/
93 o 0:e82fb8d02bbf ROOT
93 o 0:e82fb8d02bbf ROOT
94
94
95 $ hg log -G --hidden -v
95 $ hg log -G --hidden -v
96 @ 4:391a2bf12b1b c_B1
96 @ 4:391a2bf12b1b c_B1
97 |
97 |
98 | x 3:e5d7dda7cd28 c_B0 [rewritten as 4:391a2bf12b1b by server (at 1970-01-01 00:00 +0000)]
98 | x 3:e5d7dda7cd28 c_B0 [rewritten as 4:391a2bf12b1b by server (at 1970-01-01 00:00 +0000)]
99 |/
99 |/
100 | o 2:7f6b0a6f5c25 c_A1
100 | o 2:7f6b0a6f5c25 c_A1
101 |/
101 |/
102 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
102 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
103 |/
103 |/
104 o 0:e82fb8d02bbf ROOT
104 o 0:e82fb8d02bbf ROOT
105
105
106 $ hg debugobsolete
106 $ hg debugobsolete
107 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
107 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
108 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 391a2bf12b1b8b05a72400ae36b26d50a091dc22 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
108 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 391a2bf12b1b8b05a72400ae36b26d50a091dc22 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
109 $ cd ..
109 $ cd ..
110
110
111 client side: create a marker between two common changesets
111 client side: create a marker between two common changesets
112 (client is not aware of the server activity yet)
112 (client is not aware of the server activity yet)
113
113
114 $ cd client
114 $ cd client
115 $ hg debugobsolete `getid 'desc("c_A1")'` `getid 'desc("c_B0")'`
115 $ hg debugobsolete `getid 'desc("c_A1")'` `getid 'desc("c_B0")'`
116 1 new obsolescence markers
116 1 new obsolescence markers
117 obsoleted 1 changesets
117 obsoleted 1 changesets
118 $ hg log -G
118 $ hg log -G
119 @ 3:e5d7dda7cd28 c_B0
119 @ 3:e5d7dda7cd28 c_B0
120 |
120 |
121 o 0:e82fb8d02bbf ROOT
121 o 0:e82fb8d02bbf ROOT
122
122
123 $ hg log -G --hidden -v
123 $ hg log -G --hidden -v
124 @ 3:e5d7dda7cd28 c_B0
124 @ 3:e5d7dda7cd28 c_B0
125 |
125 |
126 | x 2:7f6b0a6f5c25 c_A1 [rewritten as 3:e5d7dda7cd28 by client (at 1970-01-01 00:00 +0000)]
126 | x 2:7f6b0a6f5c25 c_A1 [rewritten as 3:e5d7dda7cd28 by client (at 1970-01-01 00:00 +0000)]
127 |/
127 |/
128 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
128 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
129 |/
129 |/
130 o 0:e82fb8d02bbf ROOT
130 o 0:e82fb8d02bbf ROOT
131
131
132 $ hg debugobsolete
132 $ hg debugobsolete
133 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
133 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
134 7f6b0a6f5c25345a83870963efd827c1798a5959 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'client'}
134 7f6b0a6f5c25345a83870963efd827c1798a5959 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'client'}
135
135
136 client side: pull from the server
136 client side: pull from the server
137 (the new successors should take over)
137 (the new successors should take over)
138
138
139 $ hg up 'desc("ROOT")'
139 $ hg up 'desc("ROOT")'
140 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
140 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
141 $ hg pull
141 $ hg pull --confirm --config ui.interactive=True << EOF
142 > n
143 > EOF
142 pulling from $TESTTMP/distributed-chain-building/server
144 pulling from $TESTTMP/distributed-chain-building/server
143 searching for changes
145 searching for changes
144 adding changesets
146 adding changesets
145 adding manifests
147 adding manifests
146 adding file changes
148 adding file changes
149 adding 1 changesets with 1 changes to 1 files (+1 heads)
150 1 new obsolescence markers
151 obsoleting 1 changesets
152 new changesets 391a2bf12b1b (1 drafts)
153 accept incoming changes (yn)? n
154 transaction abort!
155 rollback completed
156 abort: user aborted
157 [255]
158
159 $ hg pull --confirm --config ui.interactive=True << EOF
160 > y
161 > EOF
162 pulling from $TESTTMP/distributed-chain-building/server
163 searching for changes
164 adding changesets
165 adding manifests
166 adding file changes
167 adding 1 changesets with 1 changes to 1 files (+1 heads)
168 1 new obsolescence markers
169 obsoleting 1 changesets
170 new changesets 391a2bf12b1b (1 drafts)
171 accept incoming changes (yn)? y
147 added 1 changesets with 1 changes to 1 files (+1 heads)
172 added 1 changesets with 1 changes to 1 files (+1 heads)
148 1 new obsolescence markers
173 1 new obsolescence markers
149 obsoleted 1 changesets
174 obsoleted 1 changesets
150 new changesets 391a2bf12b1b (1 drafts)
175 new changesets 391a2bf12b1b (1 drafts)
151 (run 'hg heads' to see heads)
176 (run 'hg heads' to see heads)
152 $ hg log -G
177 $ hg log -G
153 o 4:391a2bf12b1b c_B1
178 o 4:391a2bf12b1b c_B1
154 |
179 |
155 @ 0:e82fb8d02bbf ROOT
180 @ 0:e82fb8d02bbf ROOT
156
181
157 $ hg log -G --hidden -v
182 $ hg log -G --hidden -v
158 o 4:391a2bf12b1b c_B1
183 o 4:391a2bf12b1b c_B1
159 |
184 |
160 | x 3:e5d7dda7cd28 c_B0 [rewritten as 4:391a2bf12b1b by server (at 1970-01-01 00:00 +0000)]
185 | x 3:e5d7dda7cd28 c_B0 [rewritten as 4:391a2bf12b1b by server (at 1970-01-01 00:00 +0000)]
161 |/
186 |/
162 | x 2:7f6b0a6f5c25 c_A1 [rewritten as 3:e5d7dda7cd28 by client (at 1970-01-01 00:00 +0000)]
187 | x 2:7f6b0a6f5c25 c_A1 [rewritten as 3:e5d7dda7cd28 by client (at 1970-01-01 00:00 +0000)]
163 |/
188 |/
164 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
189 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
165 |/
190 |/
166 @ 0:e82fb8d02bbf ROOT
191 @ 0:e82fb8d02bbf ROOT
167
192
168 $ hg debugobsolete
193 $ hg debugobsolete
169 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
194 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
170 7f6b0a6f5c25345a83870963efd827c1798a5959 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'client'}
195 7f6b0a6f5c25345a83870963efd827c1798a5959 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'client'}
171 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 391a2bf12b1b8b05a72400ae36b26d50a091dc22 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
196 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 391a2bf12b1b8b05a72400ae36b26d50a091dc22 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
172
197
173 server side: receive client push
198 server side: receive client push
174 (the other way around, pushing to the server, the obsolete changesets stay
199 (the other way around, pushing to the server, the obsolete changesets stay
175 obsolete on the server side but the marker is sent out.)
200 obsolete on the server side but the marker is sent out.)
176
201
177 $ hg rollback
202 $ hg rollback
178 repository tip rolled back to revision 3 (undo pull)
203 repository tip rolled back to revision 3 (undo pull)
179 $ hg push -f
204 $ hg push -f
180 pushing to $TESTTMP/distributed-chain-building/server
205 pushing to $TESTTMP/distributed-chain-building/server
181 searching for changes
206 searching for changes
182 adding changesets
207 adding changesets
183 adding manifests
208 adding manifests
184 adding file changes
209 adding file changes
185 added 0 changesets with 0 changes to 1 files
210 added 0 changesets with 0 changes to 1 files
186 1 new obsolescence markers
211 1 new obsolescence markers
187 obsoleted 1 changesets
212 obsoleted 1 changesets
188 $ hg -R ../server/ log -G
213 $ hg -R ../server/ log -G
189 @ 4:391a2bf12b1b c_B1
214 @ 4:391a2bf12b1b c_B1
190 |
215 |
191 o 0:e82fb8d02bbf ROOT
216 o 0:e82fb8d02bbf ROOT
192
217
193 $ hg -R ../server/ log -G --hidden -v
218 $ hg -R ../server/ log -G --hidden -v
194 @ 4:391a2bf12b1b c_B1
219 @ 4:391a2bf12b1b c_B1
195 |
220 |
196 | x 3:e5d7dda7cd28 c_B0 [rewritten as 4:391a2bf12b1b by server (at 1970-01-01 00:00 +0000)]
221 | x 3:e5d7dda7cd28 c_B0 [rewritten as 4:391a2bf12b1b by server (at 1970-01-01 00:00 +0000)]
197 |/
222 |/
198 | x 2:7f6b0a6f5c25 c_A1 [rewritten as 3:e5d7dda7cd28 by client (at 1970-01-01 00:00 +0000)]
223 | x 2:7f6b0a6f5c25 c_A1 [rewritten as 3:e5d7dda7cd28 by client (at 1970-01-01 00:00 +0000)]
199 |/
224 |/
200 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
225 | x 1:e1b46f0f979f c_A0 [rewritten as 2:7f6b0a6f5c25 by server (at 1970-01-01 00:00 +0000)]
201 |/
226 |/
202 o 0:e82fb8d02bbf ROOT
227 o 0:e82fb8d02bbf ROOT
203
228
204 $ hg debugobsolete
229 $ hg debugobsolete
205 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
230 e1b46f0f979f52748347ff8729c59f2ef56e6fe2 7f6b0a6f5c25345a83870963efd827c1798a5959 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'server'}
206 7f6b0a6f5c25345a83870963efd827c1798a5959 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'client'}
231 7f6b0a6f5c25345a83870963efd827c1798a5959 e5d7dda7cd28e6b3f79437e5b8122a38ece0255c 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'client'}
207 $ cd ..
232 $ cd ..
208
233
209 Check getting changesets after getting the markers
234 Check getting changesets after getting the markers
210 =================================================
235 =================================================
211
236
212 This test case covers the scenario where commits are received -after- we
237 This test case covers the scenario where commits are received -after- we
213 received some obsolescence markers turning them obsolete.
238 received some obsolescence markers turning them obsolete.
214
239
215 For example, we pull some successors from a repository (with associated
240 For example, we pull some successors from a repository (with associated
216 predecessors marker chain) and then later we pull some intermediate
241 predecessors marker chain) and then later we pull some intermediate
217 precedessors changeset from another repository. Obsolescence markers must
242 precedessors changeset from another repository. Obsolescence markers must
218 apply to the intermediate changeset. They have to be obsolete (and hidden).
243 apply to the intermediate changeset. They have to be obsolete (and hidden).
219
244
220 Avoiding pulling the changeset in the first place is a tricky decision because
245 Avoiding pulling the changeset in the first place is a tricky decision because
221 there could be non-obsolete ancestors that need to be pulled, but the
246 there could be non-obsolete ancestors that need to be pulled, but the
222 discovery cannot currently find these (this is not the case in this tests). In
247 discovery cannot currently find these (this is not the case in this tests). In
223 addition, we could also have to pull the changeset because they have children.
248 addition, we could also have to pull the changeset because they have children.
224 In this case, they would not be hidden (yet) because of the orphan descendant,
249 In this case, they would not be hidden (yet) because of the orphan descendant,
225 but they would still have to be obsolete. (This is not tested in this case
250 but they would still have to be obsolete. (This is not tested in this case
226 either).
251 either).
227
252
228 $ mkdir distributed-chain-building
253 $ mkdir distributed-chain-building
229 $ cd distributed-chain-building
254 $ cd distributed-chain-building
230 $ hg init server
255 $ hg init server
231 $ cd server
256 $ cd server
232 $ cat << EOF >> .hg/hgrc
257 $ cat << EOF >> .hg/hgrc
233 > [ui]
258 > [ui]
234 > username = server
259 > username = server
235 > EOF
260 > EOF
236 $ mkcommit ROOT
261 $ mkcommit ROOT
237 $ cd ..
262 $ cd ..
238 $ hg clone server repo-Alice
263 $ hg clone server repo-Alice
239 updating to branch default
264 updating to branch default
240 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 $ cat << EOF >> repo-Alice/.hg/hgrc
266 $ cat << EOF >> repo-Alice/.hg/hgrc
242 > [ui]
267 > [ui]
243 > username = alice
268 > username = alice
244 > EOF
269 > EOF
245 $ hg clone server repo-Bob
270 $ hg clone server repo-Bob
246 updating to branch default
271 updating to branch default
247 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 $ cat << EOF >> repo-Bob/.hg/hgrc
273 $ cat << EOF >> repo-Bob/.hg/hgrc
249 > [ui]
274 > [ui]
250 > username = bob
275 > username = bob
251 > EOF
276 > EOF
252 $ hg clone server repo-Celeste
277 $ hg clone server repo-Celeste
253 updating to branch default
278 updating to branch default
254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 $ cat << EOF >> repo-Celeste/.hg/hgrc
280 $ cat << EOF >> repo-Celeste/.hg/hgrc
256 > [ui]
281 > [ui]
257 > username = celeste
282 > username = celeste
258 > EOF
283 > EOF
259
284
260 Create some changesets locally
285 Create some changesets locally
261
286
262 $ cd repo-Alice
287 $ cd repo-Alice
263 $ mkcommit c_A0
288 $ mkcommit c_A0
264 $ mkcommit c_B0
289 $ mkcommit c_B0
265 $ cd ..
290 $ cd ..
266
291
267 Bob pulls from Alice and rewrites them
292 Bob pulls from Alice and rewrites them
268
293
269 $ cd repo-Bob
294 $ cd repo-Bob
270 $ hg pull ../repo-Alice
295 $ hg pull ../repo-Alice
271 pulling from ../repo-Alice
296 pulling from ../repo-Alice
272 searching for changes
297 searching for changes
273 adding changesets
298 adding changesets
274 adding manifests
299 adding manifests
275 adding file changes
300 adding file changes
276 added 2 changesets with 2 changes to 2 files
301 added 2 changesets with 2 changes to 2 files
277 new changesets d33b0a3a6464:ef908e42ce65 (2 drafts)
302 new changesets d33b0a3a6464:ef908e42ce65 (2 drafts)
278 (run 'hg update' to get a working copy)
303 (run 'hg update' to get a working copy)
279 $ hg up 'desc("c_A")'
304 $ hg up 'desc("c_A")'
280 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
305 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
281 $ hg commit --amend -m 'c_A1'
306 $ hg commit --amend -m 'c_A1'
282 1 new orphan changesets
307 1 new orphan changesets
283 $ hg rebase -r 'desc("c_B0")' -d . # no easy way to rewrite the message with the rebase
308 $ hg rebase -r 'desc("c_B0")' -d . # no easy way to rewrite the message with the rebase
284 rebasing 2:ef908e42ce65 "c_B0"
309 rebasing 2:ef908e42ce65 "c_B0"
285 $ hg up
310 $ hg up
286 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 $ hg commit --amend -m 'c_B1'
312 $ hg commit --amend -m 'c_B1'
288 $ hg log -G
313 $ hg log -G
289 @ 5:956063ac4557 c_B1
314 @ 5:956063ac4557 c_B1
290 |
315 |
291 o 3:5b5708a437f2 c_A1
316 o 3:5b5708a437f2 c_A1
292 |
317 |
293 o 0:e82fb8d02bbf ROOT
318 o 0:e82fb8d02bbf ROOT
294
319
295 $ hg log -G --hidden -v
320 $ hg log -G --hidden -v
296 @ 5:956063ac4557 c_B1
321 @ 5:956063ac4557 c_B1
297 |
322 |
298 | x 4:5ffb9e311b35 c_B0 [rewritten using amend as 5:956063ac4557 by bob (at 1970-01-01 00:00 +0000)]
323 | x 4:5ffb9e311b35 c_B0 [rewritten using amend as 5:956063ac4557 by bob (at 1970-01-01 00:00 +0000)]
299 |/
324 |/
300 o 3:5b5708a437f2 c_A1
325 o 3:5b5708a437f2 c_A1
301 |
326 |
302 | x 2:ef908e42ce65 c_B0 [rewritten using rebase as 4:5ffb9e311b35 by bob (at 1970-01-01 00:00 +0000)]
327 | x 2:ef908e42ce65 c_B0 [rewritten using rebase as 4:5ffb9e311b35 by bob (at 1970-01-01 00:00 +0000)]
303 | |
328 | |
304 | x 1:d33b0a3a6464 c_A0 [rewritten using amend as 3:5b5708a437f2 by bob (at 1970-01-01 00:00 +0000)]
329 | x 1:d33b0a3a6464 c_A0 [rewritten using amend as 3:5b5708a437f2 by bob (at 1970-01-01 00:00 +0000)]
305 |/
330 |/
306 o 0:e82fb8d02bbf ROOT
331 o 0:e82fb8d02bbf ROOT
307
332
308 $ hg debugobsolete
333 $ hg debugobsolete
309 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
334 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
310 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
335 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
311 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
336 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
312 $ cd ..
337 $ cd ..
313
338
314 Celeste pulls from Bob and rewrites them again
339 Celeste pulls from Bob and rewrites them again
315
340
316 $ cd repo-Celeste
341 $ cd repo-Celeste
317 $ hg pull ../repo-Bob
342 $ hg pull ../repo-Bob
318 pulling from ../repo-Bob
343 pulling from ../repo-Bob
319 searching for changes
344 searching for changes
320 adding changesets
345 adding changesets
321 adding manifests
346 adding manifests
322 adding file changes
347 adding file changes
323 added 2 changesets with 2 changes to 2 files
348 added 2 changesets with 2 changes to 2 files
324 3 new obsolescence markers
349 3 new obsolescence markers
325 new changesets 5b5708a437f2:956063ac4557 (2 drafts)
350 new changesets 5b5708a437f2:956063ac4557 (2 drafts)
326 (run 'hg update' to get a working copy)
351 (run 'hg update' to get a working copy)
327 $ hg up 'desc("c_A")'
352 $ hg up 'desc("c_A")'
328 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
329 $ hg commit --amend -m 'c_A2'
354 $ hg commit --amend -m 'c_A2'
330 1 new orphan changesets
355 1 new orphan changesets
331 $ hg rebase -r 'desc("c_B1")' -d . # no easy way to rewrite the message with the rebase
356 $ hg rebase -r 'desc("c_B1")' -d . # no easy way to rewrite the message with the rebase
332 rebasing 2:956063ac4557 "c_B1"
357 rebasing 2:956063ac4557 "c_B1"
333 $ hg up
358 $ hg up
334 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
335 $ hg commit --amend -m 'c_B2'
360 $ hg commit --amend -m 'c_B2'
336 $ hg log -G
361 $ hg log -G
337 @ 5:77ae25d99ff0 c_B2
362 @ 5:77ae25d99ff0 c_B2
338 |
363 |
339 o 3:9866d64649a5 c_A2
364 o 3:9866d64649a5 c_A2
340 |
365 |
341 o 0:e82fb8d02bbf ROOT
366 o 0:e82fb8d02bbf ROOT
342
367
343 $ hg log -G --hidden -v
368 $ hg log -G --hidden -v
344 @ 5:77ae25d99ff0 c_B2
369 @ 5:77ae25d99ff0 c_B2
345 |
370 |
346 | x 4:3cf8de21cc22 c_B1 [rewritten using amend as 5:77ae25d99ff0 by celeste (at 1970-01-01 00:00 +0000)]
371 | x 4:3cf8de21cc22 c_B1 [rewritten using amend as 5:77ae25d99ff0 by celeste (at 1970-01-01 00:00 +0000)]
347 |/
372 |/
348 o 3:9866d64649a5 c_A2
373 o 3:9866d64649a5 c_A2
349 |
374 |
350 | x 2:956063ac4557 c_B1 [rewritten using rebase as 4:3cf8de21cc22 by celeste (at 1970-01-01 00:00 +0000)]
375 | x 2:956063ac4557 c_B1 [rewritten using rebase as 4:3cf8de21cc22 by celeste (at 1970-01-01 00:00 +0000)]
351 | |
376 | |
352 | x 1:5b5708a437f2 c_A1 [rewritten using amend as 3:9866d64649a5 by celeste (at 1970-01-01 00:00 +0000)]
377 | x 1:5b5708a437f2 c_A1 [rewritten using amend as 3:9866d64649a5 by celeste (at 1970-01-01 00:00 +0000)]
353 |/
378 |/
354 o 0:e82fb8d02bbf ROOT
379 o 0:e82fb8d02bbf ROOT
355
380
356 $ hg debugobsolete
381 $ hg debugobsolete
357 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
382 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
358 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
383 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
359 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
384 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
360 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
385 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
361 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
386 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
362 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
387 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
363
388
364 Celeste now pushes to the server
389 Celeste now pushes to the server
365
390
366 (note: it would be enough to just have direct Celeste -> Alice exchange here.
391 (note: it would be enough to just have direct Celeste -> Alice exchange here.
367 However using a central server seems more common)
392 However using a central server seems more common)
368
393
369 $ hg push
394 $ hg push
370 pushing to $TESTTMP/distributed-chain-building/distributed-chain-building/server
395 pushing to $TESTTMP/distributed-chain-building/distributed-chain-building/server
371 searching for changes
396 searching for changes
372 adding changesets
397 adding changesets
373 adding manifests
398 adding manifests
374 adding file changes
399 adding file changes
375 added 2 changesets with 2 changes to 2 files
400 added 2 changesets with 2 changes to 2 files
376 6 new obsolescence markers
401 6 new obsolescence markers
377 $ cd ..
402 $ cd ..
378
403
379 Now Alice pulls from the server, then from Bob
404 Now Alice pulls from the server, then from Bob
380
405
381 Alice first retrieves the new evolution of its changesets and associated markers
406 Alice first retrieves the new evolution of its changesets and associated markers
382 from the server (note: could be from Celeste directly)
407 from the server (note: could be from Celeste directly)
383
408
384 $ cd repo-Alice
409 $ cd repo-Alice
385 $ hg up 'desc(ROOT)'
410 $ hg up 'desc(ROOT)'
386 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
411 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
387 $ hg pull
412 $ hg pull
388 pulling from $TESTTMP/distributed-chain-building/distributed-chain-building/server
413 pulling from $TESTTMP/distributed-chain-building/distributed-chain-building/server
389 searching for changes
414 searching for changes
390 adding changesets
415 adding changesets
391 adding manifests
416 adding manifests
392 adding file changes
417 adding file changes
393 added 2 changesets with 0 changes to 2 files (+1 heads)
418 added 2 changesets with 0 changes to 2 files (+1 heads)
394 6 new obsolescence markers
419 6 new obsolescence markers
395 obsoleted 2 changesets
420 obsoleted 2 changesets
396 new changesets 9866d64649a5:77ae25d99ff0 (2 drafts)
421 new changesets 9866d64649a5:77ae25d99ff0 (2 drafts)
397 (run 'hg heads' to see heads)
422 (run 'hg heads' to see heads)
398 $ hg debugobsolete
423 $ hg debugobsolete
399 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
424 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
400 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
425 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
401 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
426 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
402 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
427 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
403 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
428 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
404 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
429 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
405
430
406 Then, she pulls from Bob, pulling predecessors of the changeset she has
431 Then, she pulls from Bob, pulling predecessors of the changeset she has
407 already pulled. The changesets are not obsoleted in the Bob repo yet. Their
432 already pulled. The changesets are not obsoleted in the Bob repo yet. Their
408 successors do not exist in Bob repository yet.
433 successors do not exist in Bob repository yet.
409
434
410 $ hg pull ../repo-Bob
435 $ hg pull ../repo-Bob
411 pulling from ../repo-Bob
436 pulling from ../repo-Bob
412 searching for changes
437 searching for changes
413 adding changesets
438 adding changesets
414 adding manifests
439 adding manifests
415 adding file changes
440 adding file changes
416 added 2 changesets with 0 changes to 2 files (+1 heads)
441 added 2 changesets with 0 changes to 2 files (+1 heads)
417 (2 other changesets obsolete on arrival)
442 (2 other changesets obsolete on arrival)
418 (run 'hg heads' to see heads)
443 (run 'hg heads' to see heads)
419 $ hg log -G
444 $ hg log -G
420 o 4:77ae25d99ff0 c_B2
445 o 4:77ae25d99ff0 c_B2
421 |
446 |
422 o 3:9866d64649a5 c_A2
447 o 3:9866d64649a5 c_A2
423 |
448 |
424 @ 0:e82fb8d02bbf ROOT
449 @ 0:e82fb8d02bbf ROOT
425
450
426 $ hg debugobsolete
451 $ hg debugobsolete
427 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
452 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
428 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
453 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
429 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
454 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
430 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
455 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
431 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
456 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
432 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
457 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
433
458
434 Same tests, but change coming from a bundle
459 Same tests, but change coming from a bundle
435 (testing with a bundle is interesting because absolutely no discovery or
460 (testing with a bundle is interesting because absolutely no discovery or
436 decision is made in that case, so receiving the changesets are not an option).
461 decision is made in that case, so receiving the changesets are not an option).
437
462
438 $ hg rollback
463 $ hg rollback
439 repository tip rolled back to revision 4 (undo pull)
464 repository tip rolled back to revision 4 (undo pull)
440 $ hg log -G
465 $ hg log -G
441 o 4:77ae25d99ff0 c_B2
466 o 4:77ae25d99ff0 c_B2
442 |
467 |
443 o 3:9866d64649a5 c_A2
468 o 3:9866d64649a5 c_A2
444 |
469 |
445 @ 0:e82fb8d02bbf ROOT
470 @ 0:e82fb8d02bbf ROOT
446
471
447 $ hg debugobsolete
472 $ hg debugobsolete
448 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
473 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
449 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
474 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
450 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
475 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
451 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
476 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
452 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
477 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
453 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
478 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
454 $ hg -R ../repo-Bob bundle ../step-1.hg
479 $ hg -R ../repo-Bob bundle ../step-1.hg
455 searching for changes
480 searching for changes
456 2 changesets found
481 2 changesets found
457 $ hg unbundle ../step-1.hg
482 $ hg unbundle ../step-1.hg
458 adding changesets
483 adding changesets
459 adding manifests
484 adding manifests
460 adding file changes
485 adding file changes
461 added 2 changesets with 0 changes to 2 files (+1 heads)
486 added 2 changesets with 0 changes to 2 files (+1 heads)
462 (2 other changesets obsolete on arrival)
487 (2 other changesets obsolete on arrival)
463 (run 'hg heads' to see heads)
488 (run 'hg heads' to see heads)
464 $ hg log -G
489 $ hg log -G
465 o 4:77ae25d99ff0 c_B2
490 o 4:77ae25d99ff0 c_B2
466 |
491 |
467 o 3:9866d64649a5 c_A2
492 o 3:9866d64649a5 c_A2
468 |
493 |
469 @ 0:e82fb8d02bbf ROOT
494 @ 0:e82fb8d02bbf ROOT
470
495
471 $ hg log -G --hidden -v
496 $ hg log -G --hidden -v
472 x 6:956063ac4557 c_B1 [rewritten using amend, rebase as 4:77ae25d99ff0 by celeste (at 1970-01-01 00:00 +0000)]
497 x 6:956063ac4557 c_B1 [rewritten using amend, rebase as 4:77ae25d99ff0 by celeste (at 1970-01-01 00:00 +0000)]
473 |
498 |
474 x 5:5b5708a437f2 c_A1 [rewritten using amend as 3:9866d64649a5 by celeste (at 1970-01-01 00:00 +0000)]
499 x 5:5b5708a437f2 c_A1 [rewritten using amend as 3:9866d64649a5 by celeste (at 1970-01-01 00:00 +0000)]
475 |
500 |
476 | o 4:77ae25d99ff0 c_B2
501 | o 4:77ae25d99ff0 c_B2
477 | |
502 | |
478 | o 3:9866d64649a5 c_A2
503 | o 3:9866d64649a5 c_A2
479 |/
504 |/
480 | x 2:ef908e42ce65 c_B0 [rewritten using amend, rebase as 6:956063ac4557 by bob (at 1970-01-01 00:00 +0000)]
505 | x 2:ef908e42ce65 c_B0 [rewritten using amend, rebase as 6:956063ac4557 by bob (at 1970-01-01 00:00 +0000)]
481 | |
506 | |
482 | x 1:d33b0a3a6464 c_A0 [rewritten using amend as 5:5b5708a437f2 by bob (at 1970-01-01 00:00 +0000)]
507 | x 1:d33b0a3a6464 c_A0 [rewritten using amend as 5:5b5708a437f2 by bob (at 1970-01-01 00:00 +0000)]
483 |/
508 |/
484 @ 0:e82fb8d02bbf ROOT
509 @ 0:e82fb8d02bbf ROOT
485
510
486 $ hg debugobsolete
511 $ hg debugobsolete
487 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
512 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 77ae25d99ff07889e181126b1171b94bec8e5227 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
488 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
513 5b5708a437f27665db42c5a261a539a1bcb2a8c2 9866d64649a5d9c5991fe119c7b2c33898114e10 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'celeste'}
489 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
514 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 956063ac4557828781733b2d5677a351ce856f59 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
490 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
515 956063ac4557828781733b2d5677a351ce856f59 3cf8de21cc2282186857d2266eb6b1f9cb85ecf3 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'celeste'}
491 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
516 d33b0a3a64647d79583526be8107802b1f9fedfa 5b5708a437f27665db42c5a261a539a1bcb2a8c2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'bob'}
492 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
517 ef908e42ce65ef57f970d799acaddde26f58a4cc 5ffb9e311b35f6ab6f76f667ca5d6e595645481b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'bob'}
493
518
494
519
495 Same tests, but with --rev, this prevent regressing case where `hg pull --rev
520 Same tests, but with --rev, this prevent regressing case where `hg pull --rev
496 X` has to process a X that is filtered locally.
521 X` has to process a X that is filtered locally.
497
522
498 $ hg rollback
523 $ hg rollback
499 repository tip rolled back to revision 4 (undo unbundle)
524 repository tip rolled back to revision 4 (undo unbundle)
500 $ hg pull ../repo-Bob --rev 956063ac4557
525 $ hg pull ../repo-Bob --rev 956063ac4557
501 pulling from ../repo-Bob
526 pulling from ../repo-Bob
502 searching for changes
527 searching for changes
503 adding changesets
528 adding changesets
504 adding manifests
529 adding manifests
505 adding file changes
530 adding file changes
506 added 2 changesets with 0 changes to 2 files (+1 heads)
531 added 2 changesets with 0 changes to 2 files (+1 heads)
507 (2 other changesets obsolete on arrival)
532 (2 other changesets obsolete on arrival)
508 (run 'hg heads' to see heads)
533 (run 'hg heads' to see heads)
509
534
510 With --update
535 With --update
511
536
512 $ hg rollback
537 $ hg rollback
513 repository tip rolled back to revision 4 (undo pull)
538 repository tip rolled back to revision 4 (undo pull)
514 $ hg pull ../repo-Bob --rev 956063ac4557 --update
539 $ hg pull ../repo-Bob --rev 956063ac4557 --update
515 pulling from ../repo-Bob
540 pulling from ../repo-Bob
516 searching for changes
541 searching for changes
517 adding changesets
542 adding changesets
518 adding manifests
543 adding manifests
519 adding file changes
544 adding file changes
520 added 2 changesets with 0 changes to 2 files (+1 heads)
545 added 2 changesets with 0 changes to 2 files (+1 heads)
521 (2 other changesets obsolete on arrival)
546 (2 other changesets obsolete on arrival)
522 abort: cannot update to target: filtered revision '6'!
547 abort: cannot update to target: filtered revision '6'!
523 [255]
548 [255]
524
549
525 $ cd ..
550 $ cd ..
526
551
527 Test pull report consistency
552 Test pull report consistency
528 ============================
553 ============================
529
554
530 obsolete but visible should be reported
555 obsolete but visible should be reported
531 ---------------------------------------
556 ---------------------------------------
532
557
533 Setup
558 Setup
534
559
535 $ hg init repo-a
560 $ hg init repo-a
536 $ cat << EOF >> repo-a/.hg/hgrc
561 $ cat << EOF >> repo-a/.hg/hgrc
537 > [ui]
562 > [ui]
538 > username=test
563 > username=test
539 > EOF
564 > EOF
540 $ cd repo-a
565 $ cd repo-a
541 $ hg debugbuilddag ..
566 $ hg debugbuilddag ..
542 $ hg debugobsolete `getid tip`
567 $ hg debugobsolete `getid tip`
543 1 new obsolescence markers
568 1 new obsolescence markers
544 obsoleted 1 changesets
569 obsoleted 1 changesets
545 $ cd ../
570 $ cd ../
546 $ hg clone --pull repo-a repo-b
571 $ hg clone --pull repo-a repo-b
547 requesting all changes
572 requesting all changes
548 adding changesets
573 adding changesets
549 adding manifests
574 adding manifests
550 adding file changes
575 adding file changes
551 added 1 changesets with 0 changes to 0 files
576 added 1 changesets with 0 changes to 0 files
552 new changesets 1ea73414a91b (1 drafts)
577 new changesets 1ea73414a91b (1 drafts)
553 updating to branch default
578 updating to branch default
554 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
579 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
555 $ hg -R repo-a up tip --hidden
580 $ hg -R repo-a up tip --hidden
556 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
581 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
557 updated to hidden changeset 66f7d451a68b
582 updated to hidden changeset 66f7d451a68b
558 (hidden revision '66f7d451a68b' is pruned)
583 (hidden revision '66f7d451a68b' is pruned)
559 $ hg -R repo-a branch foo
584 $ hg -R repo-a branch foo
560 marked working directory as branch foo
585 marked working directory as branch foo
561 (branches are permanent and global, did you want a bookmark?)
586 (branches are permanent and global, did you want a bookmark?)
562 $ hg -R repo-a commit -m foo
587 $ hg -R repo-a commit -m foo
563 1 new orphan changesets
588 1 new orphan changesets
564
589
565 Actual test
590 Actual test
566 (BROKEN)
591 (BROKEN)
567
592
568 $ hg -R repo-b pull
593 $ hg -R repo-b pull
569 pulling from $TESTTMP/distributed-chain-building/distributed-chain-building/repo-a
594 pulling from $TESTTMP/distributed-chain-building/distributed-chain-building/repo-a
570 searching for changes
595 searching for changes
571 adding changesets
596 adding changesets
572 adding manifests
597 adding manifests
573 adding file changes
598 adding file changes
574 added 2 changesets with 0 changes to 0 files
599 added 2 changesets with 0 changes to 0 files
575 1 new obsolescence markers
600 1 new obsolescence markers
576 1 new orphan changesets
601 1 new orphan changesets
577 new changesets 66f7d451a68b:95d586532b49 (2 drafts)
602 new changesets 66f7d451a68b:95d586532b49 (2 drafts)
578 (run 'hg update' to get a working copy)
603 (run 'hg update' to get a working copy)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now