##// END OF EJS Templates
mergedriver: delete it...
Martin von Zweigbergk -
r46091:32ce4cba default
parent child Browse files
Show More
@@ -1,7871 +1,7814 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 mergestate as mergestatemod,
49 mergestate as mergestatemod,
50 narrowspec,
50 narrowspec,
51 obsolete,
51 obsolete,
52 obsutil,
52 obsutil,
53 patch,
53 patch,
54 phases,
54 phases,
55 pycompat,
55 pycompat,
56 rcutil,
56 rcutil,
57 registrar,
57 registrar,
58 requirements,
58 requirements,
59 revsetlang,
59 revsetlang,
60 rewriteutil,
60 rewriteutil,
61 scmutil,
61 scmutil,
62 server,
62 server,
63 shelve as shelvemod,
63 shelve as shelvemod,
64 state as statemod,
64 state as statemod,
65 streamclone,
65 streamclone,
66 tags as tagsmod,
66 tags as tagsmod,
67 ui as uimod,
67 ui as uimod,
68 util,
68 util,
69 verify as verifymod,
69 verify as verifymod,
70 vfs as vfsmod,
70 vfs as vfsmod,
71 wireprotoserver,
71 wireprotoserver,
72 )
72 )
73 from .utils import (
73 from .utils import (
74 dateutil,
74 dateutil,
75 stringutil,
75 stringutil,
76 )
76 )
77
77
78 table = {}
78 table = {}
79 table.update(debugcommandsmod.command._table)
79 table.update(debugcommandsmod.command._table)
80
80
81 command = registrar.command(table)
81 command = registrar.command(table)
82 INTENT_READONLY = registrar.INTENT_READONLY
82 INTENT_READONLY = registrar.INTENT_READONLY
83
83
84 # common command options
84 # common command options
85
85
86 globalopts = [
86 globalopts = [
87 (
87 (
88 b'R',
88 b'R',
89 b'repository',
89 b'repository',
90 b'',
90 b'',
91 _(b'repository root directory or name of overlay bundle file'),
91 _(b'repository root directory or name of overlay bundle file'),
92 _(b'REPO'),
92 _(b'REPO'),
93 ),
93 ),
94 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
94 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95 (
95 (
96 b'y',
96 b'y',
97 b'noninteractive',
97 b'noninteractive',
98 None,
98 None,
99 _(
99 _(
100 b'do not prompt, automatically pick the first choice for all prompts'
100 b'do not prompt, automatically pick the first choice for all prompts'
101 ),
101 ),
102 ),
102 ),
103 (b'q', b'quiet', None, _(b'suppress output')),
103 (b'q', b'quiet', None, _(b'suppress output')),
104 (b'v', b'verbose', None, _(b'enable additional output')),
104 (b'v', b'verbose', None, _(b'enable additional output')),
105 (
105 (
106 b'',
106 b'',
107 b'color',
107 b'color',
108 b'',
108 b'',
109 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
109 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110 # and should not be translated
110 # and should not be translated
111 _(b"when to colorize (boolean, always, auto, never, or debug)"),
111 _(b"when to colorize (boolean, always, auto, never, or debug)"),
112 _(b'TYPE'),
112 _(b'TYPE'),
113 ),
113 ),
114 (
114 (
115 b'',
115 b'',
116 b'config',
116 b'config',
117 [],
117 [],
118 _(b'set/override config option (use \'section.name=value\')'),
118 _(b'set/override config option (use \'section.name=value\')'),
119 _(b'CONFIG'),
119 _(b'CONFIG'),
120 ),
120 ),
121 (b'', b'debug', None, _(b'enable debugging output')),
121 (b'', b'debug', None, _(b'enable debugging output')),
122 (b'', b'debugger', None, _(b'start debugger')),
122 (b'', b'debugger', None, _(b'start debugger')),
123 (
123 (
124 b'',
124 b'',
125 b'encoding',
125 b'encoding',
126 encoding.encoding,
126 encoding.encoding,
127 _(b'set the charset encoding'),
127 _(b'set the charset encoding'),
128 _(b'ENCODE'),
128 _(b'ENCODE'),
129 ),
129 ),
130 (
130 (
131 b'',
131 b'',
132 b'encodingmode',
132 b'encodingmode',
133 encoding.encodingmode,
133 encoding.encodingmode,
134 _(b'set the charset encoding mode'),
134 _(b'set the charset encoding mode'),
135 _(b'MODE'),
135 _(b'MODE'),
136 ),
136 ),
137 (b'', b'traceback', None, _(b'always print a traceback on exception')),
137 (b'', b'traceback', None, _(b'always print a traceback on exception')),
138 (b'', b'time', None, _(b'time how long the command takes')),
138 (b'', b'time', None, _(b'time how long the command takes')),
139 (b'', b'profile', None, _(b'print command execution profile')),
139 (b'', b'profile', None, _(b'print command execution profile')),
140 (b'', b'version', None, _(b'output version information and exit')),
140 (b'', b'version', None, _(b'output version information and exit')),
141 (b'h', b'help', None, _(b'display help and exit')),
141 (b'h', b'help', None, _(b'display help and exit')),
142 (b'', b'hidden', False, _(b'consider hidden changesets')),
142 (b'', b'hidden', False, _(b'consider hidden changesets')),
143 (
143 (
144 b'',
144 b'',
145 b'pager',
145 b'pager',
146 b'auto',
146 b'auto',
147 _(b"when to paginate (boolean, always, auto, or never)"),
147 _(b"when to paginate (boolean, always, auto, or never)"),
148 _(b'TYPE'),
148 _(b'TYPE'),
149 ),
149 ),
150 ]
150 ]
151
151
152 dryrunopts = cmdutil.dryrunopts
152 dryrunopts = cmdutil.dryrunopts
153 remoteopts = cmdutil.remoteopts
153 remoteopts = cmdutil.remoteopts
154 walkopts = cmdutil.walkopts
154 walkopts = cmdutil.walkopts
155 commitopts = cmdutil.commitopts
155 commitopts = cmdutil.commitopts
156 commitopts2 = cmdutil.commitopts2
156 commitopts2 = cmdutil.commitopts2
157 commitopts3 = cmdutil.commitopts3
157 commitopts3 = cmdutil.commitopts3
158 formatteropts = cmdutil.formatteropts
158 formatteropts = cmdutil.formatteropts
159 templateopts = cmdutil.templateopts
159 templateopts = cmdutil.templateopts
160 logopts = cmdutil.logopts
160 logopts = cmdutil.logopts
161 diffopts = cmdutil.diffopts
161 diffopts = cmdutil.diffopts
162 diffwsopts = cmdutil.diffwsopts
162 diffwsopts = cmdutil.diffwsopts
163 diffopts2 = cmdutil.diffopts2
163 diffopts2 = cmdutil.diffopts2
164 mergetoolopts = cmdutil.mergetoolopts
164 mergetoolopts = cmdutil.mergetoolopts
165 similarityopts = cmdutil.similarityopts
165 similarityopts = cmdutil.similarityopts
166 subrepoopts = cmdutil.subrepoopts
166 subrepoopts = cmdutil.subrepoopts
167 debugrevlogopts = cmdutil.debugrevlogopts
167 debugrevlogopts = cmdutil.debugrevlogopts
168
168
169 # Commands start here, listed alphabetically
169 # Commands start here, listed alphabetically
170
170
171
171
172 @command(
172 @command(
173 b'abort',
173 b'abort',
174 dryrunopts,
174 dryrunopts,
175 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
175 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176 helpbasic=True,
176 helpbasic=True,
177 )
177 )
178 def abort(ui, repo, **opts):
178 def abort(ui, repo, **opts):
179 """abort an unfinished operation (EXPERIMENTAL)
179 """abort an unfinished operation (EXPERIMENTAL)
180
180
181 Aborts a multistep operation like graft, histedit, rebase, merge,
181 Aborts a multistep operation like graft, histedit, rebase, merge,
182 and unshelve if they are in an unfinished state.
182 and unshelve if they are in an unfinished state.
183
183
184 use --dry-run/-n to dry run the command.
184 use --dry-run/-n to dry run the command.
185 """
185 """
186 dryrun = opts.get('dry_run')
186 dryrun = opts.get('dry_run')
187 abortstate = cmdutil.getunfinishedstate(repo)
187 abortstate = cmdutil.getunfinishedstate(repo)
188 if not abortstate:
188 if not abortstate:
189 raise error.Abort(_(b'no operation in progress'))
189 raise error.Abort(_(b'no operation in progress'))
190 if not abortstate.abortfunc:
190 if not abortstate.abortfunc:
191 raise error.Abort(
191 raise error.Abort(
192 (
192 (
193 _(b"%s in progress but does not support 'hg abort'")
193 _(b"%s in progress but does not support 'hg abort'")
194 % (abortstate._opname)
194 % (abortstate._opname)
195 ),
195 ),
196 hint=abortstate.hint(),
196 hint=abortstate.hint(),
197 )
197 )
198 if dryrun:
198 if dryrun:
199 ui.status(
199 ui.status(
200 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
200 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201 )
201 )
202 return
202 return
203 return abortstate.abortfunc(ui, repo)
203 return abortstate.abortfunc(ui, repo)
204
204
205
205
206 @command(
206 @command(
207 b'add',
207 b'add',
208 walkopts + subrepoopts + dryrunopts,
208 walkopts + subrepoopts + dryrunopts,
209 _(b'[OPTION]... [FILE]...'),
209 _(b'[OPTION]... [FILE]...'),
210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211 helpbasic=True,
211 helpbasic=True,
212 inferrepo=True,
212 inferrepo=True,
213 )
213 )
214 def add(ui, repo, *pats, **opts):
214 def add(ui, repo, *pats, **opts):
215 """add the specified files on the next commit
215 """add the specified files on the next commit
216
216
217 Schedule files to be version controlled and added to the
217 Schedule files to be version controlled and added to the
218 repository.
218 repository.
219
219
220 The files will be added to the repository at the next commit. To
220 The files will be added to the repository at the next commit. To
221 undo an add before that, see :hg:`forget`.
221 undo an add before that, see :hg:`forget`.
222
222
223 If no names are given, add all files to the repository (except
223 If no names are given, add all files to the repository (except
224 files matching ``.hgignore``).
224 files matching ``.hgignore``).
225
225
226 .. container:: verbose
226 .. container:: verbose
227
227
228 Examples:
228 Examples:
229
229
230 - New (unknown) files are added
230 - New (unknown) files are added
231 automatically by :hg:`add`::
231 automatically by :hg:`add`::
232
232
233 $ ls
233 $ ls
234 foo.c
234 foo.c
235 $ hg status
235 $ hg status
236 ? foo.c
236 ? foo.c
237 $ hg add
237 $ hg add
238 adding foo.c
238 adding foo.c
239 $ hg status
239 $ hg status
240 A foo.c
240 A foo.c
241
241
242 - Specific files to be added can be specified::
242 - Specific files to be added can be specified::
243
243
244 $ ls
244 $ ls
245 bar.c foo.c
245 bar.c foo.c
246 $ hg status
246 $ hg status
247 ? bar.c
247 ? bar.c
248 ? foo.c
248 ? foo.c
249 $ hg add bar.c
249 $ hg add bar.c
250 $ hg status
250 $ hg status
251 A bar.c
251 A bar.c
252 ? foo.c
252 ? foo.c
253
253
254 Returns 0 if all files are successfully added.
254 Returns 0 if all files are successfully added.
255 """
255 """
256
256
257 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
257 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
258 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
259 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260 return rejected and 1 or 0
260 return rejected and 1 or 0
261
261
262
262
263 @command(
263 @command(
264 b'addremove',
264 b'addremove',
265 similarityopts + subrepoopts + walkopts + dryrunopts,
265 similarityopts + subrepoopts + walkopts + dryrunopts,
266 _(b'[OPTION]... [FILE]...'),
266 _(b'[OPTION]... [FILE]...'),
267 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
267 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268 inferrepo=True,
268 inferrepo=True,
269 )
269 )
270 def addremove(ui, repo, *pats, **opts):
270 def addremove(ui, repo, *pats, **opts):
271 """add all new files, delete all missing files
271 """add all new files, delete all missing files
272
272
273 Add all new files and remove all missing files from the
273 Add all new files and remove all missing files from the
274 repository.
274 repository.
275
275
276 Unless names are given, new files are ignored if they match any of
276 Unless names are given, new files are ignored if they match any of
277 the patterns in ``.hgignore``. As with add, these changes take
277 the patterns in ``.hgignore``. As with add, these changes take
278 effect at the next commit.
278 effect at the next commit.
279
279
280 Use the -s/--similarity option to detect renamed files. This
280 Use the -s/--similarity option to detect renamed files. This
281 option takes a percentage between 0 (disabled) and 100 (files must
281 option takes a percentage between 0 (disabled) and 100 (files must
282 be identical) as its parameter. With a parameter greater than 0,
282 be identical) as its parameter. With a parameter greater than 0,
283 this compares every removed file with every added file and records
283 this compares every removed file with every added file and records
284 those similar enough as renames. Detecting renamed files this way
284 those similar enough as renames. Detecting renamed files this way
285 can be expensive. After using this option, :hg:`status -C` can be
285 can be expensive. After using this option, :hg:`status -C` can be
286 used to check which files were identified as moved or renamed. If
286 used to check which files were identified as moved or renamed. If
287 not specified, -s/--similarity defaults to 100 and only renames of
287 not specified, -s/--similarity defaults to 100 and only renames of
288 identical files are detected.
288 identical files are detected.
289
289
290 .. container:: verbose
290 .. container:: verbose
291
291
292 Examples:
292 Examples:
293
293
294 - A number of files (bar.c and foo.c) are new,
294 - A number of files (bar.c and foo.c) are new,
295 while foobar.c has been removed (without using :hg:`remove`)
295 while foobar.c has been removed (without using :hg:`remove`)
296 from the repository::
296 from the repository::
297
297
298 $ ls
298 $ ls
299 bar.c foo.c
299 bar.c foo.c
300 $ hg status
300 $ hg status
301 ! foobar.c
301 ! foobar.c
302 ? bar.c
302 ? bar.c
303 ? foo.c
303 ? foo.c
304 $ hg addremove
304 $ hg addremove
305 adding bar.c
305 adding bar.c
306 adding foo.c
306 adding foo.c
307 removing foobar.c
307 removing foobar.c
308 $ hg status
308 $ hg status
309 A bar.c
309 A bar.c
310 A foo.c
310 A foo.c
311 R foobar.c
311 R foobar.c
312
312
313 - A file foobar.c was moved to foo.c without using :hg:`rename`.
313 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 Afterwards, it was edited slightly::
314 Afterwards, it was edited slightly::
315
315
316 $ ls
316 $ ls
317 foo.c
317 foo.c
318 $ hg status
318 $ hg status
319 ! foobar.c
319 ! foobar.c
320 ? foo.c
320 ? foo.c
321 $ hg addremove --similarity 90
321 $ hg addremove --similarity 90
322 removing foobar.c
322 removing foobar.c
323 adding foo.c
323 adding foo.c
324 recording removal of foobar.c as rename to foo.c (94% similar)
324 recording removal of foobar.c as rename to foo.c (94% similar)
325 $ hg status -C
325 $ hg status -C
326 A foo.c
326 A foo.c
327 foobar.c
327 foobar.c
328 R foobar.c
328 R foobar.c
329
329
330 Returns 0 if all files are successfully added.
330 Returns 0 if all files are successfully added.
331 """
331 """
332 opts = pycompat.byteskwargs(opts)
332 opts = pycompat.byteskwargs(opts)
333 if not opts.get(b'similarity'):
333 if not opts.get(b'similarity'):
334 opts[b'similarity'] = b'100'
334 opts[b'similarity'] = b'100'
335 matcher = scmutil.match(repo[None], pats, opts)
335 matcher = scmutil.match(repo[None], pats, opts)
336 relative = scmutil.anypats(pats, opts)
336 relative = scmutil.anypats(pats, opts)
337 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
337 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
338 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339
339
340
340
341 @command(
341 @command(
342 b'annotate|blame',
342 b'annotate|blame',
343 [
343 [
344 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
344 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (
345 (
346 b'',
346 b'',
347 b'follow',
347 b'follow',
348 None,
348 None,
349 _(b'follow copies/renames and list the filename (DEPRECATED)'),
349 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 ),
350 ),
351 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
351 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'a', b'text', None, _(b'treat all files as text')),
352 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'u', b'user', None, _(b'list the author (long with -v)')),
353 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'f', b'file', None, _(b'list the filename')),
354 (b'f', b'file', None, _(b'list the filename')),
355 (b'd', b'date', None, _(b'list the date (short with -q)')),
355 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'n', b'number', None, _(b'list the revision number (default)')),
356 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'c', b'changeset', None, _(b'list the changeset')),
357 (b'c', b'changeset', None, _(b'list the changeset')),
358 (
358 (
359 b'l',
359 b'l',
360 b'line-number',
360 b'line-number',
361 None,
361 None,
362 _(b'show line number at the first appearance'),
362 _(b'show line number at the first appearance'),
363 ),
363 ),
364 (
364 (
365 b'',
365 b'',
366 b'skip',
366 b'skip',
367 [],
367 [],
368 _(b'revset to not display (EXPERIMENTAL)'),
368 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'REV'),
369 _(b'REV'),
370 ),
370 ),
371 ]
371 ]
372 + diffwsopts
372 + diffwsopts
373 + walkopts
373 + walkopts
374 + formatteropts,
374 + formatteropts,
375 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
375 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 helpcategory=command.CATEGORY_FILE_CONTENTS,
376 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpbasic=True,
377 helpbasic=True,
378 inferrepo=True,
378 inferrepo=True,
379 )
379 )
380 def annotate(ui, repo, *pats, **opts):
380 def annotate(ui, repo, *pats, **opts):
381 """show changeset information by line for each file
381 """show changeset information by line for each file
382
382
383 List changes in files, showing the revision id responsible for
383 List changes in files, showing the revision id responsible for
384 each line.
384 each line.
385
385
386 This command is useful for discovering when a change was made and
386 This command is useful for discovering when a change was made and
387 by whom.
387 by whom.
388
388
389 If you include --file, --user, or --date, the revision number is
389 If you include --file, --user, or --date, the revision number is
390 suppressed unless you also include --number.
390 suppressed unless you also include --number.
391
391
392 Without the -a/--text option, annotate will avoid processing files
392 Without the -a/--text option, annotate will avoid processing files
393 it detects as binary. With -a, annotate will annotate the file
393 it detects as binary. With -a, annotate will annotate the file
394 anyway, although the results will probably be neither useful
394 anyway, although the results will probably be neither useful
395 nor desirable.
395 nor desirable.
396
396
397 .. container:: verbose
397 .. container:: verbose
398
398
399 Template:
399 Template:
400
400
401 The following keywords are supported in addition to the common template
401 The following keywords are supported in addition to the common template
402 keywords and functions. See also :hg:`help templates`.
402 keywords and functions. See also :hg:`help templates`.
403
403
404 :lines: List of lines with annotation data.
404 :lines: List of lines with annotation data.
405 :path: String. Repository-absolute path of the specified file.
405 :path: String. Repository-absolute path of the specified file.
406
406
407 And each entry of ``{lines}`` provides the following sub-keywords in
407 And each entry of ``{lines}`` provides the following sub-keywords in
408 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
408 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409
409
410 :line: String. Line content.
410 :line: String. Line content.
411 :lineno: Integer. Line number at that revision.
411 :lineno: Integer. Line number at that revision.
412 :path: String. Repository-absolute path of the file at that revision.
412 :path: String. Repository-absolute path of the file at that revision.
413
413
414 See :hg:`help templates.operators` for the list expansion syntax.
414 See :hg:`help templates.operators` for the list expansion syntax.
415
415
416 Returns 0 on success.
416 Returns 0 on success.
417 """
417 """
418 opts = pycompat.byteskwargs(opts)
418 opts = pycompat.byteskwargs(opts)
419 if not pats:
419 if not pats:
420 raise error.Abort(_(b'at least one filename or pattern is required'))
420 raise error.Abort(_(b'at least one filename or pattern is required'))
421
421
422 if opts.get(b'follow'):
422 if opts.get(b'follow'):
423 # --follow is deprecated and now just an alias for -f/--file
423 # --follow is deprecated and now just an alias for -f/--file
424 # to mimic the behavior of Mercurial before version 1.5
424 # to mimic the behavior of Mercurial before version 1.5
425 opts[b'file'] = True
425 opts[b'file'] = True
426
426
427 if (
427 if (
428 not opts.get(b'user')
428 not opts.get(b'user')
429 and not opts.get(b'changeset')
429 and not opts.get(b'changeset')
430 and not opts.get(b'date')
430 and not opts.get(b'date')
431 and not opts.get(b'file')
431 and not opts.get(b'file')
432 ):
432 ):
433 opts[b'number'] = True
433 opts[b'number'] = True
434
434
435 linenumber = opts.get(b'line_number') is not None
435 linenumber = opts.get(b'line_number') is not None
436 if (
436 if (
437 linenumber
437 linenumber
438 and (not opts.get(b'changeset'))
438 and (not opts.get(b'changeset'))
439 and (not opts.get(b'number'))
439 and (not opts.get(b'number'))
440 ):
440 ):
441 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
441 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
442
442
443 rev = opts.get(b'rev')
443 rev = opts.get(b'rev')
444 if rev:
444 if rev:
445 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
445 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
446 ctx = scmutil.revsingle(repo, rev)
446 ctx = scmutil.revsingle(repo, rev)
447
447
448 ui.pager(b'annotate')
448 ui.pager(b'annotate')
449 rootfm = ui.formatter(b'annotate', opts)
449 rootfm = ui.formatter(b'annotate', opts)
450 if ui.debugflag:
450 if ui.debugflag:
451 shorthex = pycompat.identity
451 shorthex = pycompat.identity
452 else:
452 else:
453
453
454 def shorthex(h):
454 def shorthex(h):
455 return h[:12]
455 return h[:12]
456
456
457 if ui.quiet:
457 if ui.quiet:
458 datefunc = dateutil.shortdate
458 datefunc = dateutil.shortdate
459 else:
459 else:
460 datefunc = dateutil.datestr
460 datefunc = dateutil.datestr
461 if ctx.rev() is None:
461 if ctx.rev() is None:
462 if opts.get(b'changeset'):
462 if opts.get(b'changeset'):
463 # omit "+" suffix which is appended to node hex
463 # omit "+" suffix which is appended to node hex
464 def formatrev(rev):
464 def formatrev(rev):
465 if rev == wdirrev:
465 if rev == wdirrev:
466 return b'%d' % ctx.p1().rev()
466 return b'%d' % ctx.p1().rev()
467 else:
467 else:
468 return b'%d' % rev
468 return b'%d' % rev
469
469
470 else:
470 else:
471
471
472 def formatrev(rev):
472 def formatrev(rev):
473 if rev == wdirrev:
473 if rev == wdirrev:
474 return b'%d+' % ctx.p1().rev()
474 return b'%d+' % ctx.p1().rev()
475 else:
475 else:
476 return b'%d ' % rev
476 return b'%d ' % rev
477
477
478 def formathex(h):
478 def formathex(h):
479 if h == wdirhex:
479 if h == wdirhex:
480 return b'%s+' % shorthex(hex(ctx.p1().node()))
480 return b'%s+' % shorthex(hex(ctx.p1().node()))
481 else:
481 else:
482 return b'%s ' % shorthex(h)
482 return b'%s ' % shorthex(h)
483
483
484 else:
484 else:
485 formatrev = b'%d'.__mod__
485 formatrev = b'%d'.__mod__
486 formathex = shorthex
486 formathex = shorthex
487
487
488 opmap = [
488 opmap = [
489 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
489 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
490 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
490 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
491 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
491 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
492 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
492 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
493 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
493 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
494 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
494 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
495 ]
495 ]
496 opnamemap = {
496 opnamemap = {
497 b'rev': b'number',
497 b'rev': b'number',
498 b'node': b'changeset',
498 b'node': b'changeset',
499 b'path': b'file',
499 b'path': b'file',
500 b'lineno': b'line_number',
500 b'lineno': b'line_number',
501 }
501 }
502
502
503 if rootfm.isplain():
503 if rootfm.isplain():
504
504
505 def makefunc(get, fmt):
505 def makefunc(get, fmt):
506 return lambda x: fmt(get(x))
506 return lambda x: fmt(get(x))
507
507
508 else:
508 else:
509
509
510 def makefunc(get, fmt):
510 def makefunc(get, fmt):
511 return get
511 return get
512
512
513 datahint = rootfm.datahint()
513 datahint = rootfm.datahint()
514 funcmap = [
514 funcmap = [
515 (makefunc(get, fmt), sep)
515 (makefunc(get, fmt), sep)
516 for fn, sep, get, fmt in opmap
516 for fn, sep, get, fmt in opmap
517 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
517 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
518 ]
518 ]
519 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
519 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
520 fields = b' '.join(
520 fields = b' '.join(
521 fn
521 fn
522 for fn, sep, get, fmt in opmap
522 for fn, sep, get, fmt in opmap
523 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
523 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
524 )
524 )
525
525
526 def bad(x, y):
526 def bad(x, y):
527 raise error.Abort(b"%s: %s" % (x, y))
527 raise error.Abort(b"%s: %s" % (x, y))
528
528
529 m = scmutil.match(ctx, pats, opts, badfn=bad)
529 m = scmutil.match(ctx, pats, opts, badfn=bad)
530
530
531 follow = not opts.get(b'no_follow')
531 follow = not opts.get(b'no_follow')
532 diffopts = patch.difffeatureopts(
532 diffopts = patch.difffeatureopts(
533 ui, opts, section=b'annotate', whitespace=True
533 ui, opts, section=b'annotate', whitespace=True
534 )
534 )
535 skiprevs = opts.get(b'skip')
535 skiprevs = opts.get(b'skip')
536 if skiprevs:
536 if skiprevs:
537 skiprevs = scmutil.revrange(repo, skiprevs)
537 skiprevs = scmutil.revrange(repo, skiprevs)
538
538
539 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
539 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
540 for abs in ctx.walk(m):
540 for abs in ctx.walk(m):
541 fctx = ctx[abs]
541 fctx = ctx[abs]
542 rootfm.startitem()
542 rootfm.startitem()
543 rootfm.data(path=abs)
543 rootfm.data(path=abs)
544 if not opts.get(b'text') and fctx.isbinary():
544 if not opts.get(b'text') and fctx.isbinary():
545 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
545 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
546 continue
546 continue
547
547
548 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
548 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
549 lines = fctx.annotate(
549 lines = fctx.annotate(
550 follow=follow, skiprevs=skiprevs, diffopts=diffopts
550 follow=follow, skiprevs=skiprevs, diffopts=diffopts
551 )
551 )
552 if not lines:
552 if not lines:
553 fm.end()
553 fm.end()
554 continue
554 continue
555 formats = []
555 formats = []
556 pieces = []
556 pieces = []
557
557
558 for f, sep in funcmap:
558 for f, sep in funcmap:
559 l = [f(n) for n in lines]
559 l = [f(n) for n in lines]
560 if fm.isplain():
560 if fm.isplain():
561 sizes = [encoding.colwidth(x) for x in l]
561 sizes = [encoding.colwidth(x) for x in l]
562 ml = max(sizes)
562 ml = max(sizes)
563 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
563 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
564 else:
564 else:
565 formats.append([b'%s'] * len(l))
565 formats.append([b'%s'] * len(l))
566 pieces.append(l)
566 pieces.append(l)
567
567
568 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
568 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
569 fm.startitem()
569 fm.startitem()
570 fm.context(fctx=n.fctx)
570 fm.context(fctx=n.fctx)
571 fm.write(fields, b"".join(f), *p)
571 fm.write(fields, b"".join(f), *p)
572 if n.skip:
572 if n.skip:
573 fmt = b"* %s"
573 fmt = b"* %s"
574 else:
574 else:
575 fmt = b": %s"
575 fmt = b": %s"
576 fm.write(b'line', fmt, n.text)
576 fm.write(b'line', fmt, n.text)
577
577
578 if not lines[-1].text.endswith(b'\n'):
578 if not lines[-1].text.endswith(b'\n'):
579 fm.plain(b'\n')
579 fm.plain(b'\n')
580 fm.end()
580 fm.end()
581
581
582 rootfm.end()
582 rootfm.end()
583
583
584
584
585 @command(
585 @command(
586 b'archive',
586 b'archive',
587 [
587 [
588 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
588 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
589 (
589 (
590 b'p',
590 b'p',
591 b'prefix',
591 b'prefix',
592 b'',
592 b'',
593 _(b'directory prefix for files in archive'),
593 _(b'directory prefix for files in archive'),
594 _(b'PREFIX'),
594 _(b'PREFIX'),
595 ),
595 ),
596 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
596 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
597 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
597 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
598 ]
598 ]
599 + subrepoopts
599 + subrepoopts
600 + walkopts,
600 + walkopts,
601 _(b'[OPTION]... DEST'),
601 _(b'[OPTION]... DEST'),
602 helpcategory=command.CATEGORY_IMPORT_EXPORT,
602 helpcategory=command.CATEGORY_IMPORT_EXPORT,
603 )
603 )
604 def archive(ui, repo, dest, **opts):
604 def archive(ui, repo, dest, **opts):
605 '''create an unversioned archive of a repository revision
605 '''create an unversioned archive of a repository revision
606
606
607 By default, the revision used is the parent of the working
607 By default, the revision used is the parent of the working
608 directory; use -r/--rev to specify a different revision.
608 directory; use -r/--rev to specify a different revision.
609
609
610 The archive type is automatically detected based on file
610 The archive type is automatically detected based on file
611 extension (to override, use -t/--type).
611 extension (to override, use -t/--type).
612
612
613 .. container:: verbose
613 .. container:: verbose
614
614
615 Examples:
615 Examples:
616
616
617 - create a zip file containing the 1.0 release::
617 - create a zip file containing the 1.0 release::
618
618
619 hg archive -r 1.0 project-1.0.zip
619 hg archive -r 1.0 project-1.0.zip
620
620
621 - create a tarball excluding .hg files::
621 - create a tarball excluding .hg files::
622
622
623 hg archive project.tar.gz -X ".hg*"
623 hg archive project.tar.gz -X ".hg*"
624
624
625 Valid types are:
625 Valid types are:
626
626
627 :``files``: a directory full of files (default)
627 :``files``: a directory full of files (default)
628 :``tar``: tar archive, uncompressed
628 :``tar``: tar archive, uncompressed
629 :``tbz2``: tar archive, compressed using bzip2
629 :``tbz2``: tar archive, compressed using bzip2
630 :``tgz``: tar archive, compressed using gzip
630 :``tgz``: tar archive, compressed using gzip
631 :``txz``: tar archive, compressed using lzma (only in Python 3)
631 :``txz``: tar archive, compressed using lzma (only in Python 3)
632 :``uzip``: zip archive, uncompressed
632 :``uzip``: zip archive, uncompressed
633 :``zip``: zip archive, compressed using deflate
633 :``zip``: zip archive, compressed using deflate
634
634
635 The exact name of the destination archive or directory is given
635 The exact name of the destination archive or directory is given
636 using a format string; see :hg:`help export` for details.
636 using a format string; see :hg:`help export` for details.
637
637
638 Each member added to an archive file has a directory prefix
638 Each member added to an archive file has a directory prefix
639 prepended. Use -p/--prefix to specify a format string for the
639 prepended. Use -p/--prefix to specify a format string for the
640 prefix. The default is the basename of the archive, with suffixes
640 prefix. The default is the basename of the archive, with suffixes
641 removed.
641 removed.
642
642
643 Returns 0 on success.
643 Returns 0 on success.
644 '''
644 '''
645
645
646 opts = pycompat.byteskwargs(opts)
646 opts = pycompat.byteskwargs(opts)
647 rev = opts.get(b'rev')
647 rev = opts.get(b'rev')
648 if rev:
648 if rev:
649 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
649 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
650 ctx = scmutil.revsingle(repo, rev)
650 ctx = scmutil.revsingle(repo, rev)
651 if not ctx:
651 if not ctx:
652 raise error.Abort(_(b'no working directory: please specify a revision'))
652 raise error.Abort(_(b'no working directory: please specify a revision'))
653 node = ctx.node()
653 node = ctx.node()
654 dest = cmdutil.makefilename(ctx, dest)
654 dest = cmdutil.makefilename(ctx, dest)
655 if os.path.realpath(dest) == repo.root:
655 if os.path.realpath(dest) == repo.root:
656 raise error.Abort(_(b'repository root cannot be destination'))
656 raise error.Abort(_(b'repository root cannot be destination'))
657
657
658 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
658 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
659 prefix = opts.get(b'prefix')
659 prefix = opts.get(b'prefix')
660
660
661 if dest == b'-':
661 if dest == b'-':
662 if kind == b'files':
662 if kind == b'files':
663 raise error.Abort(_(b'cannot archive plain files to stdout'))
663 raise error.Abort(_(b'cannot archive plain files to stdout'))
664 dest = cmdutil.makefileobj(ctx, dest)
664 dest = cmdutil.makefileobj(ctx, dest)
665 if not prefix:
665 if not prefix:
666 prefix = os.path.basename(repo.root) + b'-%h'
666 prefix = os.path.basename(repo.root) + b'-%h'
667
667
668 prefix = cmdutil.makefilename(ctx, prefix)
668 prefix = cmdutil.makefilename(ctx, prefix)
669 match = scmutil.match(ctx, [], opts)
669 match = scmutil.match(ctx, [], opts)
670 archival.archive(
670 archival.archive(
671 repo,
671 repo,
672 dest,
672 dest,
673 node,
673 node,
674 kind,
674 kind,
675 not opts.get(b'no_decode'),
675 not opts.get(b'no_decode'),
676 match,
676 match,
677 prefix,
677 prefix,
678 subrepos=opts.get(b'subrepos'),
678 subrepos=opts.get(b'subrepos'),
679 )
679 )
680
680
681
681
682 @command(
682 @command(
683 b'backout',
683 b'backout',
684 [
684 [
685 (
685 (
686 b'',
686 b'',
687 b'merge',
687 b'merge',
688 None,
688 None,
689 _(b'merge with old dirstate parent after backout'),
689 _(b'merge with old dirstate parent after backout'),
690 ),
690 ),
691 (
691 (
692 b'',
692 b'',
693 b'commit',
693 b'commit',
694 None,
694 None,
695 _(b'commit if no conflicts were encountered (DEPRECATED)'),
695 _(b'commit if no conflicts were encountered (DEPRECATED)'),
696 ),
696 ),
697 (b'', b'no-commit', None, _(b'do not commit')),
697 (b'', b'no-commit', None, _(b'do not commit')),
698 (
698 (
699 b'',
699 b'',
700 b'parent',
700 b'parent',
701 b'',
701 b'',
702 _(b'parent to choose when backing out merge (DEPRECATED)'),
702 _(b'parent to choose when backing out merge (DEPRECATED)'),
703 _(b'REV'),
703 _(b'REV'),
704 ),
704 ),
705 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
705 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
706 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
706 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
707 ]
707 ]
708 + mergetoolopts
708 + mergetoolopts
709 + walkopts
709 + walkopts
710 + commitopts
710 + commitopts
711 + commitopts2,
711 + commitopts2,
712 _(b'[OPTION]... [-r] REV'),
712 _(b'[OPTION]... [-r] REV'),
713 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
713 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
714 )
714 )
715 def backout(ui, repo, node=None, rev=None, **opts):
715 def backout(ui, repo, node=None, rev=None, **opts):
716 '''reverse effect of earlier changeset
716 '''reverse effect of earlier changeset
717
717
718 Prepare a new changeset with the effect of REV undone in the
718 Prepare a new changeset with the effect of REV undone in the
719 current working directory. If no conflicts were encountered,
719 current working directory. If no conflicts were encountered,
720 it will be committed immediately.
720 it will be committed immediately.
721
721
722 If REV is the parent of the working directory, then this new changeset
722 If REV is the parent of the working directory, then this new changeset
723 is committed automatically (unless --no-commit is specified).
723 is committed automatically (unless --no-commit is specified).
724
724
725 .. note::
725 .. note::
726
726
727 :hg:`backout` cannot be used to fix either an unwanted or
727 :hg:`backout` cannot be used to fix either an unwanted or
728 incorrect merge.
728 incorrect merge.
729
729
730 .. container:: verbose
730 .. container:: verbose
731
731
732 Examples:
732 Examples:
733
733
734 - Reverse the effect of the parent of the working directory.
734 - Reverse the effect of the parent of the working directory.
735 This backout will be committed immediately::
735 This backout will be committed immediately::
736
736
737 hg backout -r .
737 hg backout -r .
738
738
739 - Reverse the effect of previous bad revision 23::
739 - Reverse the effect of previous bad revision 23::
740
740
741 hg backout -r 23
741 hg backout -r 23
742
742
743 - Reverse the effect of previous bad revision 23 and
743 - Reverse the effect of previous bad revision 23 and
744 leave changes uncommitted::
744 leave changes uncommitted::
745
745
746 hg backout -r 23 --no-commit
746 hg backout -r 23 --no-commit
747 hg commit -m "Backout revision 23"
747 hg commit -m "Backout revision 23"
748
748
749 By default, the pending changeset will have one parent,
749 By default, the pending changeset will have one parent,
750 maintaining a linear history. With --merge, the pending
750 maintaining a linear history. With --merge, the pending
751 changeset will instead have two parents: the old parent of the
751 changeset will instead have two parents: the old parent of the
752 working directory and a new child of REV that simply undoes REV.
752 working directory and a new child of REV that simply undoes REV.
753
753
754 Before version 1.7, the behavior without --merge was equivalent
754 Before version 1.7, the behavior without --merge was equivalent
755 to specifying --merge followed by :hg:`update --clean .` to
755 to specifying --merge followed by :hg:`update --clean .` to
756 cancel the merge and leave the child of REV as a head to be
756 cancel the merge and leave the child of REV as a head to be
757 merged separately.
757 merged separately.
758
758
759 See :hg:`help dates` for a list of formats valid for -d/--date.
759 See :hg:`help dates` for a list of formats valid for -d/--date.
760
760
761 See :hg:`help revert` for a way to restore files to the state
761 See :hg:`help revert` for a way to restore files to the state
762 of another revision.
762 of another revision.
763
763
764 Returns 0 on success, 1 if nothing to backout or there are unresolved
764 Returns 0 on success, 1 if nothing to backout or there are unresolved
765 files.
765 files.
766 '''
766 '''
767 with repo.wlock(), repo.lock():
767 with repo.wlock(), repo.lock():
768 return _dobackout(ui, repo, node, rev, **opts)
768 return _dobackout(ui, repo, node, rev, **opts)
769
769
770
770
771 def _dobackout(ui, repo, node=None, rev=None, **opts):
771 def _dobackout(ui, repo, node=None, rev=None, **opts):
772 opts = pycompat.byteskwargs(opts)
772 opts = pycompat.byteskwargs(opts)
773 if opts.get(b'commit') and opts.get(b'no_commit'):
773 if opts.get(b'commit') and opts.get(b'no_commit'):
774 raise error.Abort(_(b"cannot use --commit with --no-commit"))
774 raise error.Abort(_(b"cannot use --commit with --no-commit"))
775 if opts.get(b'merge') and opts.get(b'no_commit'):
775 if opts.get(b'merge') and opts.get(b'no_commit'):
776 raise error.Abort(_(b"cannot use --merge with --no-commit"))
776 raise error.Abort(_(b"cannot use --merge with --no-commit"))
777
777
778 if rev and node:
778 if rev and node:
779 raise error.Abort(_(b"please specify just one revision"))
779 raise error.Abort(_(b"please specify just one revision"))
780
780
781 if not rev:
781 if not rev:
782 rev = node
782 rev = node
783
783
784 if not rev:
784 if not rev:
785 raise error.Abort(_(b"please specify a revision to backout"))
785 raise error.Abort(_(b"please specify a revision to backout"))
786
786
787 date = opts.get(b'date')
787 date = opts.get(b'date')
788 if date:
788 if date:
789 opts[b'date'] = dateutil.parsedate(date)
789 opts[b'date'] = dateutil.parsedate(date)
790
790
791 cmdutil.checkunfinished(repo)
791 cmdutil.checkunfinished(repo)
792 cmdutil.bailifchanged(repo)
792 cmdutil.bailifchanged(repo)
793 node = scmutil.revsingle(repo, rev).node()
793 node = scmutil.revsingle(repo, rev).node()
794
794
795 op1, op2 = repo.dirstate.parents()
795 op1, op2 = repo.dirstate.parents()
796 if not repo.changelog.isancestor(node, op1):
796 if not repo.changelog.isancestor(node, op1):
797 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
797 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
798
798
799 p1, p2 = repo.changelog.parents(node)
799 p1, p2 = repo.changelog.parents(node)
800 if p1 == nullid:
800 if p1 == nullid:
801 raise error.Abort(_(b'cannot backout a change with no parents'))
801 raise error.Abort(_(b'cannot backout a change with no parents'))
802 if p2 != nullid:
802 if p2 != nullid:
803 if not opts.get(b'parent'):
803 if not opts.get(b'parent'):
804 raise error.Abort(_(b'cannot backout a merge changeset'))
804 raise error.Abort(_(b'cannot backout a merge changeset'))
805 p = repo.lookup(opts[b'parent'])
805 p = repo.lookup(opts[b'parent'])
806 if p not in (p1, p2):
806 if p not in (p1, p2):
807 raise error.Abort(
807 raise error.Abort(
808 _(b'%s is not a parent of %s') % (short(p), short(node))
808 _(b'%s is not a parent of %s') % (short(p), short(node))
809 )
809 )
810 parent = p
810 parent = p
811 else:
811 else:
812 if opts.get(b'parent'):
812 if opts.get(b'parent'):
813 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
813 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
814 parent = p1
814 parent = p1
815
815
816 # the backout should appear on the same branch
816 # the backout should appear on the same branch
817 branch = repo.dirstate.branch()
817 branch = repo.dirstate.branch()
818 bheads = repo.branchheads(branch)
818 bheads = repo.branchheads(branch)
819 rctx = scmutil.revsingle(repo, hex(parent))
819 rctx = scmutil.revsingle(repo, hex(parent))
820 if not opts.get(b'merge') and op1 != node:
820 if not opts.get(b'merge') and op1 != node:
821 with dirstateguard.dirstateguard(repo, b'backout'):
821 with dirstateguard.dirstateguard(repo, b'backout'):
822 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
822 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
823 with ui.configoverride(overrides, b'backout'):
823 with ui.configoverride(overrides, b'backout'):
824 stats = mergemod.update(
824 stats = mergemod.update(
825 repo,
825 repo,
826 parent,
826 parent,
827 branchmerge=True,
827 branchmerge=True,
828 force=True,
828 force=True,
829 ancestor=node,
829 ancestor=node,
830 mergeancestor=False,
830 mergeancestor=False,
831 )
831 )
832 repo.setparents(op1, op2)
832 repo.setparents(op1, op2)
833 hg._showstats(repo, stats)
833 hg._showstats(repo, stats)
834 if stats.unresolvedcount:
834 if stats.unresolvedcount:
835 repo.ui.status(
835 repo.ui.status(
836 _(b"use 'hg resolve' to retry unresolved file merges\n")
836 _(b"use 'hg resolve' to retry unresolved file merges\n")
837 )
837 )
838 return 1
838 return 1
839 else:
839 else:
840 hg.clean(repo, node, show_stats=False)
840 hg.clean(repo, node, show_stats=False)
841 repo.dirstate.setbranch(branch)
841 repo.dirstate.setbranch(branch)
842 cmdutil.revert(ui, repo, rctx)
842 cmdutil.revert(ui, repo, rctx)
843
843
844 if opts.get(b'no_commit'):
844 if opts.get(b'no_commit'):
845 msg = _(b"changeset %s backed out, don't forget to commit.\n")
845 msg = _(b"changeset %s backed out, don't forget to commit.\n")
846 ui.status(msg % short(node))
846 ui.status(msg % short(node))
847 return 0
847 return 0
848
848
849 def commitfunc(ui, repo, message, match, opts):
849 def commitfunc(ui, repo, message, match, opts):
850 editform = b'backout'
850 editform = b'backout'
851 e = cmdutil.getcommiteditor(
851 e = cmdutil.getcommiteditor(
852 editform=editform, **pycompat.strkwargs(opts)
852 editform=editform, **pycompat.strkwargs(opts)
853 )
853 )
854 if not message:
854 if not message:
855 # we don't translate commit messages
855 # we don't translate commit messages
856 message = b"Backed out changeset %s" % short(node)
856 message = b"Backed out changeset %s" % short(node)
857 e = cmdutil.getcommiteditor(edit=True, editform=editform)
857 e = cmdutil.getcommiteditor(edit=True, editform=editform)
858 return repo.commit(
858 return repo.commit(
859 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
859 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
860 )
860 )
861
861
862 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
862 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
863 if not newnode:
863 if not newnode:
864 ui.status(_(b"nothing changed\n"))
864 ui.status(_(b"nothing changed\n"))
865 return 1
865 return 1
866 cmdutil.commitstatus(repo, newnode, branch, bheads)
866 cmdutil.commitstatus(repo, newnode, branch, bheads)
867
867
868 def nice(node):
868 def nice(node):
869 return b'%d:%s' % (repo.changelog.rev(node), short(node))
869 return b'%d:%s' % (repo.changelog.rev(node), short(node))
870
870
871 ui.status(
871 ui.status(
872 _(b'changeset %s backs out changeset %s\n')
872 _(b'changeset %s backs out changeset %s\n')
873 % (nice(repo.changelog.tip()), nice(node))
873 % (nice(repo.changelog.tip()), nice(node))
874 )
874 )
875 if opts.get(b'merge') and op1 != node:
875 if opts.get(b'merge') and op1 != node:
876 hg.clean(repo, op1, show_stats=False)
876 hg.clean(repo, op1, show_stats=False)
877 ui.status(
877 ui.status(
878 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
878 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
879 )
879 )
880 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
880 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
881 with ui.configoverride(overrides, b'backout'):
881 with ui.configoverride(overrides, b'backout'):
882 return hg.merge(repo[b'tip'])
882 return hg.merge(repo[b'tip'])
883 return 0
883 return 0
884
884
885
885
886 @command(
886 @command(
887 b'bisect',
887 b'bisect',
888 [
888 [
889 (b'r', b'reset', False, _(b'reset bisect state')),
889 (b'r', b'reset', False, _(b'reset bisect state')),
890 (b'g', b'good', False, _(b'mark changeset good')),
890 (b'g', b'good', False, _(b'mark changeset good')),
891 (b'b', b'bad', False, _(b'mark changeset bad')),
891 (b'b', b'bad', False, _(b'mark changeset bad')),
892 (b's', b'skip', False, _(b'skip testing changeset')),
892 (b's', b'skip', False, _(b'skip testing changeset')),
893 (b'e', b'extend', False, _(b'extend the bisect range')),
893 (b'e', b'extend', False, _(b'extend the bisect range')),
894 (
894 (
895 b'c',
895 b'c',
896 b'command',
896 b'command',
897 b'',
897 b'',
898 _(b'use command to check changeset state'),
898 _(b'use command to check changeset state'),
899 _(b'CMD'),
899 _(b'CMD'),
900 ),
900 ),
901 (b'U', b'noupdate', False, _(b'do not update to target')),
901 (b'U', b'noupdate', False, _(b'do not update to target')),
902 ],
902 ],
903 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
903 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
904 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
904 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
905 )
905 )
906 def bisect(
906 def bisect(
907 ui,
907 ui,
908 repo,
908 repo,
909 rev=None,
909 rev=None,
910 extra=None,
910 extra=None,
911 command=None,
911 command=None,
912 reset=None,
912 reset=None,
913 good=None,
913 good=None,
914 bad=None,
914 bad=None,
915 skip=None,
915 skip=None,
916 extend=None,
916 extend=None,
917 noupdate=None,
917 noupdate=None,
918 ):
918 ):
919 """subdivision search of changesets
919 """subdivision search of changesets
920
920
921 This command helps to find changesets which introduce problems. To
921 This command helps to find changesets which introduce problems. To
922 use, mark the earliest changeset you know exhibits the problem as
922 use, mark the earliest changeset you know exhibits the problem as
923 bad, then mark the latest changeset which is free from the problem
923 bad, then mark the latest changeset which is free from the problem
924 as good. Bisect will update your working directory to a revision
924 as good. Bisect will update your working directory to a revision
925 for testing (unless the -U/--noupdate option is specified). Once
925 for testing (unless the -U/--noupdate option is specified). Once
926 you have performed tests, mark the working directory as good or
926 you have performed tests, mark the working directory as good or
927 bad, and bisect will either update to another candidate changeset
927 bad, and bisect will either update to another candidate changeset
928 or announce that it has found the bad revision.
928 or announce that it has found the bad revision.
929
929
930 As a shortcut, you can also use the revision argument to mark a
930 As a shortcut, you can also use the revision argument to mark a
931 revision as good or bad without checking it out first.
931 revision as good or bad without checking it out first.
932
932
933 If you supply a command, it will be used for automatic bisection.
933 If you supply a command, it will be used for automatic bisection.
934 The environment variable HG_NODE will contain the ID of the
934 The environment variable HG_NODE will contain the ID of the
935 changeset being tested. The exit status of the command will be
935 changeset being tested. The exit status of the command will be
936 used to mark revisions as good or bad: status 0 means good, 125
936 used to mark revisions as good or bad: status 0 means good, 125
937 means to skip the revision, 127 (command not found) will abort the
937 means to skip the revision, 127 (command not found) will abort the
938 bisection, and any other non-zero exit status means the revision
938 bisection, and any other non-zero exit status means the revision
939 is bad.
939 is bad.
940
940
941 .. container:: verbose
941 .. container:: verbose
942
942
943 Some examples:
943 Some examples:
944
944
945 - start a bisection with known bad revision 34, and good revision 12::
945 - start a bisection with known bad revision 34, and good revision 12::
946
946
947 hg bisect --bad 34
947 hg bisect --bad 34
948 hg bisect --good 12
948 hg bisect --good 12
949
949
950 - advance the current bisection by marking current revision as good or
950 - advance the current bisection by marking current revision as good or
951 bad::
951 bad::
952
952
953 hg bisect --good
953 hg bisect --good
954 hg bisect --bad
954 hg bisect --bad
955
955
956 - mark the current revision, or a known revision, to be skipped (e.g. if
956 - mark the current revision, or a known revision, to be skipped (e.g. if
957 that revision is not usable because of another issue)::
957 that revision is not usable because of another issue)::
958
958
959 hg bisect --skip
959 hg bisect --skip
960 hg bisect --skip 23
960 hg bisect --skip 23
961
961
962 - skip all revisions that do not touch directories ``foo`` or ``bar``::
962 - skip all revisions that do not touch directories ``foo`` or ``bar``::
963
963
964 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
964 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
965
965
966 - forget the current bisection::
966 - forget the current bisection::
967
967
968 hg bisect --reset
968 hg bisect --reset
969
969
970 - use 'make && make tests' to automatically find the first broken
970 - use 'make && make tests' to automatically find the first broken
971 revision::
971 revision::
972
972
973 hg bisect --reset
973 hg bisect --reset
974 hg bisect --bad 34
974 hg bisect --bad 34
975 hg bisect --good 12
975 hg bisect --good 12
976 hg bisect --command "make && make tests"
976 hg bisect --command "make && make tests"
977
977
978 - see all changesets whose states are already known in the current
978 - see all changesets whose states are already known in the current
979 bisection::
979 bisection::
980
980
981 hg log -r "bisect(pruned)"
981 hg log -r "bisect(pruned)"
982
982
983 - see the changeset currently being bisected (especially useful
983 - see the changeset currently being bisected (especially useful
984 if running with -U/--noupdate)::
984 if running with -U/--noupdate)::
985
985
986 hg log -r "bisect(current)"
986 hg log -r "bisect(current)"
987
987
988 - see all changesets that took part in the current bisection::
988 - see all changesets that took part in the current bisection::
989
989
990 hg log -r "bisect(range)"
990 hg log -r "bisect(range)"
991
991
992 - you can even get a nice graph::
992 - you can even get a nice graph::
993
993
994 hg log --graph -r "bisect(range)"
994 hg log --graph -r "bisect(range)"
995
995
996 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
996 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
997
997
998 Returns 0 on success.
998 Returns 0 on success.
999 """
999 """
1000 # backward compatibility
1000 # backward compatibility
1001 if rev in b"good bad reset init".split():
1001 if rev in b"good bad reset init".split():
1002 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1002 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1003 cmd, rev, extra = rev, extra, None
1003 cmd, rev, extra = rev, extra, None
1004 if cmd == b"good":
1004 if cmd == b"good":
1005 good = True
1005 good = True
1006 elif cmd == b"bad":
1006 elif cmd == b"bad":
1007 bad = True
1007 bad = True
1008 else:
1008 else:
1009 reset = True
1009 reset = True
1010 elif extra:
1010 elif extra:
1011 raise error.Abort(_(b'incompatible arguments'))
1011 raise error.Abort(_(b'incompatible arguments'))
1012
1012
1013 incompatibles = {
1013 incompatibles = {
1014 b'--bad': bad,
1014 b'--bad': bad,
1015 b'--command': bool(command),
1015 b'--command': bool(command),
1016 b'--extend': extend,
1016 b'--extend': extend,
1017 b'--good': good,
1017 b'--good': good,
1018 b'--reset': reset,
1018 b'--reset': reset,
1019 b'--skip': skip,
1019 b'--skip': skip,
1020 }
1020 }
1021
1021
1022 enabled = [x for x in incompatibles if incompatibles[x]]
1022 enabled = [x for x in incompatibles if incompatibles[x]]
1023
1023
1024 if len(enabled) > 1:
1024 if len(enabled) > 1:
1025 raise error.Abort(
1025 raise error.Abort(
1026 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1026 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1027 )
1027 )
1028
1028
1029 if reset:
1029 if reset:
1030 hbisect.resetstate(repo)
1030 hbisect.resetstate(repo)
1031 return
1031 return
1032
1032
1033 state = hbisect.load_state(repo)
1033 state = hbisect.load_state(repo)
1034
1034
1035 # update state
1035 # update state
1036 if good or bad or skip:
1036 if good or bad or skip:
1037 if rev:
1037 if rev:
1038 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1038 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1039 else:
1039 else:
1040 nodes = [repo.lookup(b'.')]
1040 nodes = [repo.lookup(b'.')]
1041 if good:
1041 if good:
1042 state[b'good'] += nodes
1042 state[b'good'] += nodes
1043 elif bad:
1043 elif bad:
1044 state[b'bad'] += nodes
1044 state[b'bad'] += nodes
1045 elif skip:
1045 elif skip:
1046 state[b'skip'] += nodes
1046 state[b'skip'] += nodes
1047 hbisect.save_state(repo, state)
1047 hbisect.save_state(repo, state)
1048 if not (state[b'good'] and state[b'bad']):
1048 if not (state[b'good'] and state[b'bad']):
1049 return
1049 return
1050
1050
1051 def mayupdate(repo, node, show_stats=True):
1051 def mayupdate(repo, node, show_stats=True):
1052 """common used update sequence"""
1052 """common used update sequence"""
1053 if noupdate:
1053 if noupdate:
1054 return
1054 return
1055 cmdutil.checkunfinished(repo)
1055 cmdutil.checkunfinished(repo)
1056 cmdutil.bailifchanged(repo)
1056 cmdutil.bailifchanged(repo)
1057 return hg.clean(repo, node, show_stats=show_stats)
1057 return hg.clean(repo, node, show_stats=show_stats)
1058
1058
1059 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1059 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1060
1060
1061 if command:
1061 if command:
1062 changesets = 1
1062 changesets = 1
1063 if noupdate:
1063 if noupdate:
1064 try:
1064 try:
1065 node = state[b'current'][0]
1065 node = state[b'current'][0]
1066 except LookupError:
1066 except LookupError:
1067 raise error.Abort(
1067 raise error.Abort(
1068 _(
1068 _(
1069 b'current bisect revision is unknown - '
1069 b'current bisect revision is unknown - '
1070 b'start a new bisect to fix'
1070 b'start a new bisect to fix'
1071 )
1071 )
1072 )
1072 )
1073 else:
1073 else:
1074 node, p2 = repo.dirstate.parents()
1074 node, p2 = repo.dirstate.parents()
1075 if p2 != nullid:
1075 if p2 != nullid:
1076 raise error.Abort(_(b'current bisect revision is a merge'))
1076 raise error.Abort(_(b'current bisect revision is a merge'))
1077 if rev:
1077 if rev:
1078 node = repo[scmutil.revsingle(repo, rev, node)].node()
1078 node = repo[scmutil.revsingle(repo, rev, node)].node()
1079 with hbisect.restore_state(repo, state, node):
1079 with hbisect.restore_state(repo, state, node):
1080 while changesets:
1080 while changesets:
1081 # update state
1081 # update state
1082 state[b'current'] = [node]
1082 state[b'current'] = [node]
1083 hbisect.save_state(repo, state)
1083 hbisect.save_state(repo, state)
1084 status = ui.system(
1084 status = ui.system(
1085 command,
1085 command,
1086 environ={b'HG_NODE': hex(node)},
1086 environ={b'HG_NODE': hex(node)},
1087 blockedtag=b'bisect_check',
1087 blockedtag=b'bisect_check',
1088 )
1088 )
1089 if status == 125:
1089 if status == 125:
1090 transition = b"skip"
1090 transition = b"skip"
1091 elif status == 0:
1091 elif status == 0:
1092 transition = b"good"
1092 transition = b"good"
1093 # status < 0 means process was killed
1093 # status < 0 means process was killed
1094 elif status == 127:
1094 elif status == 127:
1095 raise error.Abort(_(b"failed to execute %s") % command)
1095 raise error.Abort(_(b"failed to execute %s") % command)
1096 elif status < 0:
1096 elif status < 0:
1097 raise error.Abort(_(b"%s killed") % command)
1097 raise error.Abort(_(b"%s killed") % command)
1098 else:
1098 else:
1099 transition = b"bad"
1099 transition = b"bad"
1100 state[transition].append(node)
1100 state[transition].append(node)
1101 ctx = repo[node]
1101 ctx = repo[node]
1102 ui.status(
1102 ui.status(
1103 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1103 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1104 )
1104 )
1105 hbisect.checkstate(state)
1105 hbisect.checkstate(state)
1106 # bisect
1106 # bisect
1107 nodes, changesets, bgood = hbisect.bisect(repo, state)
1107 nodes, changesets, bgood = hbisect.bisect(repo, state)
1108 # update to next check
1108 # update to next check
1109 node = nodes[0]
1109 node = nodes[0]
1110 mayupdate(repo, node, show_stats=False)
1110 mayupdate(repo, node, show_stats=False)
1111 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1111 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1112 return
1112 return
1113
1113
1114 hbisect.checkstate(state)
1114 hbisect.checkstate(state)
1115
1115
1116 # actually bisect
1116 # actually bisect
1117 nodes, changesets, good = hbisect.bisect(repo, state)
1117 nodes, changesets, good = hbisect.bisect(repo, state)
1118 if extend:
1118 if extend:
1119 if not changesets:
1119 if not changesets:
1120 extendnode = hbisect.extendrange(repo, state, nodes, good)
1120 extendnode = hbisect.extendrange(repo, state, nodes, good)
1121 if extendnode is not None:
1121 if extendnode is not None:
1122 ui.write(
1122 ui.write(
1123 _(b"Extending search to changeset %d:%s\n")
1123 _(b"Extending search to changeset %d:%s\n")
1124 % (extendnode.rev(), extendnode)
1124 % (extendnode.rev(), extendnode)
1125 )
1125 )
1126 state[b'current'] = [extendnode.node()]
1126 state[b'current'] = [extendnode.node()]
1127 hbisect.save_state(repo, state)
1127 hbisect.save_state(repo, state)
1128 return mayupdate(repo, extendnode.node())
1128 return mayupdate(repo, extendnode.node())
1129 raise error.Abort(_(b"nothing to extend"))
1129 raise error.Abort(_(b"nothing to extend"))
1130
1130
1131 if changesets == 0:
1131 if changesets == 0:
1132 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1132 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1133 else:
1133 else:
1134 assert len(nodes) == 1 # only a single node can be tested next
1134 assert len(nodes) == 1 # only a single node can be tested next
1135 node = nodes[0]
1135 node = nodes[0]
1136 # compute the approximate number of remaining tests
1136 # compute the approximate number of remaining tests
1137 tests, size = 0, 2
1137 tests, size = 0, 2
1138 while size <= changesets:
1138 while size <= changesets:
1139 tests, size = tests + 1, size * 2
1139 tests, size = tests + 1, size * 2
1140 rev = repo.changelog.rev(node)
1140 rev = repo.changelog.rev(node)
1141 ui.write(
1141 ui.write(
1142 _(
1142 _(
1143 b"Testing changeset %d:%s "
1143 b"Testing changeset %d:%s "
1144 b"(%d changesets remaining, ~%d tests)\n"
1144 b"(%d changesets remaining, ~%d tests)\n"
1145 )
1145 )
1146 % (rev, short(node), changesets, tests)
1146 % (rev, short(node), changesets, tests)
1147 )
1147 )
1148 state[b'current'] = [node]
1148 state[b'current'] = [node]
1149 hbisect.save_state(repo, state)
1149 hbisect.save_state(repo, state)
1150 return mayupdate(repo, node)
1150 return mayupdate(repo, node)
1151
1151
1152
1152
1153 @command(
1153 @command(
1154 b'bookmarks|bookmark',
1154 b'bookmarks|bookmark',
1155 [
1155 [
1156 (b'f', b'force', False, _(b'force')),
1156 (b'f', b'force', False, _(b'force')),
1157 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1157 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1158 (b'd', b'delete', False, _(b'delete a given bookmark')),
1158 (b'd', b'delete', False, _(b'delete a given bookmark')),
1159 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1159 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1160 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1160 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1161 (b'l', b'list', False, _(b'list existing bookmarks')),
1161 (b'l', b'list', False, _(b'list existing bookmarks')),
1162 ]
1162 ]
1163 + formatteropts,
1163 + formatteropts,
1164 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1164 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1166 )
1166 )
1167 def bookmark(ui, repo, *names, **opts):
1167 def bookmark(ui, repo, *names, **opts):
1168 '''create a new bookmark or list existing bookmarks
1168 '''create a new bookmark or list existing bookmarks
1169
1169
1170 Bookmarks are labels on changesets to help track lines of development.
1170 Bookmarks are labels on changesets to help track lines of development.
1171 Bookmarks are unversioned and can be moved, renamed and deleted.
1171 Bookmarks are unversioned and can be moved, renamed and deleted.
1172 Deleting or moving a bookmark has no effect on the associated changesets.
1172 Deleting or moving a bookmark has no effect on the associated changesets.
1173
1173
1174 Creating or updating to a bookmark causes it to be marked as 'active'.
1174 Creating or updating to a bookmark causes it to be marked as 'active'.
1175 The active bookmark is indicated with a '*'.
1175 The active bookmark is indicated with a '*'.
1176 When a commit is made, the active bookmark will advance to the new commit.
1176 When a commit is made, the active bookmark will advance to the new commit.
1177 A plain :hg:`update` will also advance an active bookmark, if possible.
1177 A plain :hg:`update` will also advance an active bookmark, if possible.
1178 Updating away from a bookmark will cause it to be deactivated.
1178 Updating away from a bookmark will cause it to be deactivated.
1179
1179
1180 Bookmarks can be pushed and pulled between repositories (see
1180 Bookmarks can be pushed and pulled between repositories (see
1181 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1181 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1182 diverged, a new 'divergent bookmark' of the form 'name@path' will
1182 diverged, a new 'divergent bookmark' of the form 'name@path' will
1183 be created. Using :hg:`merge` will resolve the divergence.
1183 be created. Using :hg:`merge` will resolve the divergence.
1184
1184
1185 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1185 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1186 the active bookmark's name.
1186 the active bookmark's name.
1187
1187
1188 A bookmark named '@' has the special property that :hg:`clone` will
1188 A bookmark named '@' has the special property that :hg:`clone` will
1189 check it out by default if it exists.
1189 check it out by default if it exists.
1190
1190
1191 .. container:: verbose
1191 .. container:: verbose
1192
1192
1193 Template:
1193 Template:
1194
1194
1195 The following keywords are supported in addition to the common template
1195 The following keywords are supported in addition to the common template
1196 keywords and functions such as ``{bookmark}``. See also
1196 keywords and functions such as ``{bookmark}``. See also
1197 :hg:`help templates`.
1197 :hg:`help templates`.
1198
1198
1199 :active: Boolean. True if the bookmark is active.
1199 :active: Boolean. True if the bookmark is active.
1200
1200
1201 Examples:
1201 Examples:
1202
1202
1203 - create an active bookmark for a new line of development::
1203 - create an active bookmark for a new line of development::
1204
1204
1205 hg book new-feature
1205 hg book new-feature
1206
1206
1207 - create an inactive bookmark as a place marker::
1207 - create an inactive bookmark as a place marker::
1208
1208
1209 hg book -i reviewed
1209 hg book -i reviewed
1210
1210
1211 - create an inactive bookmark on another changeset::
1211 - create an inactive bookmark on another changeset::
1212
1212
1213 hg book -r .^ tested
1213 hg book -r .^ tested
1214
1214
1215 - rename bookmark turkey to dinner::
1215 - rename bookmark turkey to dinner::
1216
1216
1217 hg book -m turkey dinner
1217 hg book -m turkey dinner
1218
1218
1219 - move the '@' bookmark from another branch::
1219 - move the '@' bookmark from another branch::
1220
1220
1221 hg book -f @
1221 hg book -f @
1222
1222
1223 - print only the active bookmark name::
1223 - print only the active bookmark name::
1224
1224
1225 hg book -ql .
1225 hg book -ql .
1226 '''
1226 '''
1227 opts = pycompat.byteskwargs(opts)
1227 opts = pycompat.byteskwargs(opts)
1228 force = opts.get(b'force')
1228 force = opts.get(b'force')
1229 rev = opts.get(b'rev')
1229 rev = opts.get(b'rev')
1230 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1230 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1231
1231
1232 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1232 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1233 if action:
1233 if action:
1234 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1234 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1235 elif names or rev:
1235 elif names or rev:
1236 action = b'add'
1236 action = b'add'
1237 elif inactive:
1237 elif inactive:
1238 action = b'inactive' # meaning deactivate
1238 action = b'inactive' # meaning deactivate
1239 else:
1239 else:
1240 action = b'list'
1240 action = b'list'
1241
1241
1242 cmdutil.check_incompatible_arguments(
1242 cmdutil.check_incompatible_arguments(
1243 opts, b'inactive', [b'delete', b'list']
1243 opts, b'inactive', [b'delete', b'list']
1244 )
1244 )
1245 if not names and action in {b'add', b'delete'}:
1245 if not names and action in {b'add', b'delete'}:
1246 raise error.Abort(_(b"bookmark name required"))
1246 raise error.Abort(_(b"bookmark name required"))
1247
1247
1248 if action in {b'add', b'delete', b'rename', b'inactive'}:
1248 if action in {b'add', b'delete', b'rename', b'inactive'}:
1249 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1249 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1250 if action == b'delete':
1250 if action == b'delete':
1251 names = pycompat.maplist(repo._bookmarks.expandname, names)
1251 names = pycompat.maplist(repo._bookmarks.expandname, names)
1252 bookmarks.delete(repo, tr, names)
1252 bookmarks.delete(repo, tr, names)
1253 elif action == b'rename':
1253 elif action == b'rename':
1254 if not names:
1254 if not names:
1255 raise error.Abort(_(b"new bookmark name required"))
1255 raise error.Abort(_(b"new bookmark name required"))
1256 elif len(names) > 1:
1256 elif len(names) > 1:
1257 raise error.Abort(_(b"only one new bookmark name allowed"))
1257 raise error.Abort(_(b"only one new bookmark name allowed"))
1258 oldname = repo._bookmarks.expandname(opts[b'rename'])
1258 oldname = repo._bookmarks.expandname(opts[b'rename'])
1259 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1259 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1260 elif action == b'add':
1260 elif action == b'add':
1261 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1261 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1262 elif action == b'inactive':
1262 elif action == b'inactive':
1263 if len(repo._bookmarks) == 0:
1263 if len(repo._bookmarks) == 0:
1264 ui.status(_(b"no bookmarks set\n"))
1264 ui.status(_(b"no bookmarks set\n"))
1265 elif not repo._activebookmark:
1265 elif not repo._activebookmark:
1266 ui.status(_(b"no active bookmark\n"))
1266 ui.status(_(b"no active bookmark\n"))
1267 else:
1267 else:
1268 bookmarks.deactivate(repo)
1268 bookmarks.deactivate(repo)
1269 elif action == b'list':
1269 elif action == b'list':
1270 names = pycompat.maplist(repo._bookmarks.expandname, names)
1270 names = pycompat.maplist(repo._bookmarks.expandname, names)
1271 with ui.formatter(b'bookmarks', opts) as fm:
1271 with ui.formatter(b'bookmarks', opts) as fm:
1272 bookmarks.printbookmarks(ui, repo, fm, names)
1272 bookmarks.printbookmarks(ui, repo, fm, names)
1273 else:
1273 else:
1274 raise error.ProgrammingError(b'invalid action: %s' % action)
1274 raise error.ProgrammingError(b'invalid action: %s' % action)
1275
1275
1276
1276
1277 @command(
1277 @command(
1278 b'branch',
1278 b'branch',
1279 [
1279 [
1280 (
1280 (
1281 b'f',
1281 b'f',
1282 b'force',
1282 b'force',
1283 None,
1283 None,
1284 _(b'set branch name even if it shadows an existing branch'),
1284 _(b'set branch name even if it shadows an existing branch'),
1285 ),
1285 ),
1286 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1286 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1287 (
1287 (
1288 b'r',
1288 b'r',
1289 b'rev',
1289 b'rev',
1290 [],
1290 [],
1291 _(b'change branches of the given revs (EXPERIMENTAL)'),
1291 _(b'change branches of the given revs (EXPERIMENTAL)'),
1292 ),
1292 ),
1293 ],
1293 ],
1294 _(b'[-fC] [NAME]'),
1294 _(b'[-fC] [NAME]'),
1295 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1295 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1296 )
1296 )
1297 def branch(ui, repo, label=None, **opts):
1297 def branch(ui, repo, label=None, **opts):
1298 """set or show the current branch name
1298 """set or show the current branch name
1299
1299
1300 .. note::
1300 .. note::
1301
1301
1302 Branch names are permanent and global. Use :hg:`bookmark` to create a
1302 Branch names are permanent and global. Use :hg:`bookmark` to create a
1303 light-weight bookmark instead. See :hg:`help glossary` for more
1303 light-weight bookmark instead. See :hg:`help glossary` for more
1304 information about named branches and bookmarks.
1304 information about named branches and bookmarks.
1305
1305
1306 With no argument, show the current branch name. With one argument,
1306 With no argument, show the current branch name. With one argument,
1307 set the working directory branch name (the branch will not exist
1307 set the working directory branch name (the branch will not exist
1308 in the repository until the next commit). Standard practice
1308 in the repository until the next commit). Standard practice
1309 recommends that primary development take place on the 'default'
1309 recommends that primary development take place on the 'default'
1310 branch.
1310 branch.
1311
1311
1312 Unless -f/--force is specified, branch will not let you set a
1312 Unless -f/--force is specified, branch will not let you set a
1313 branch name that already exists.
1313 branch name that already exists.
1314
1314
1315 Use -C/--clean to reset the working directory branch to that of
1315 Use -C/--clean to reset the working directory branch to that of
1316 the parent of the working directory, negating a previous branch
1316 the parent of the working directory, negating a previous branch
1317 change.
1317 change.
1318
1318
1319 Use the command :hg:`update` to switch to an existing branch. Use
1319 Use the command :hg:`update` to switch to an existing branch. Use
1320 :hg:`commit --close-branch` to mark this branch head as closed.
1320 :hg:`commit --close-branch` to mark this branch head as closed.
1321 When all heads of a branch are closed, the branch will be
1321 When all heads of a branch are closed, the branch will be
1322 considered closed.
1322 considered closed.
1323
1323
1324 Returns 0 on success.
1324 Returns 0 on success.
1325 """
1325 """
1326 opts = pycompat.byteskwargs(opts)
1326 opts = pycompat.byteskwargs(opts)
1327 revs = opts.get(b'rev')
1327 revs = opts.get(b'rev')
1328 if label:
1328 if label:
1329 label = label.strip()
1329 label = label.strip()
1330
1330
1331 if not opts.get(b'clean') and not label:
1331 if not opts.get(b'clean') and not label:
1332 if revs:
1332 if revs:
1333 raise error.Abort(_(b"no branch name specified for the revisions"))
1333 raise error.Abort(_(b"no branch name specified for the revisions"))
1334 ui.write(b"%s\n" % repo.dirstate.branch())
1334 ui.write(b"%s\n" % repo.dirstate.branch())
1335 return
1335 return
1336
1336
1337 with repo.wlock():
1337 with repo.wlock():
1338 if opts.get(b'clean'):
1338 if opts.get(b'clean'):
1339 label = repo[b'.'].branch()
1339 label = repo[b'.'].branch()
1340 repo.dirstate.setbranch(label)
1340 repo.dirstate.setbranch(label)
1341 ui.status(_(b'reset working directory to branch %s\n') % label)
1341 ui.status(_(b'reset working directory to branch %s\n') % label)
1342 elif label:
1342 elif label:
1343
1343
1344 scmutil.checknewlabel(repo, label, b'branch')
1344 scmutil.checknewlabel(repo, label, b'branch')
1345 if revs:
1345 if revs:
1346 return cmdutil.changebranch(ui, repo, revs, label, opts)
1346 return cmdutil.changebranch(ui, repo, revs, label, opts)
1347
1347
1348 if not opts.get(b'force') and label in repo.branchmap():
1348 if not opts.get(b'force') and label in repo.branchmap():
1349 if label not in [p.branch() for p in repo[None].parents()]:
1349 if label not in [p.branch() for p in repo[None].parents()]:
1350 raise error.Abort(
1350 raise error.Abort(
1351 _(b'a branch of the same name already exists'),
1351 _(b'a branch of the same name already exists'),
1352 # i18n: "it" refers to an existing branch
1352 # i18n: "it" refers to an existing branch
1353 hint=_(b"use 'hg update' to switch to it"),
1353 hint=_(b"use 'hg update' to switch to it"),
1354 )
1354 )
1355
1355
1356 repo.dirstate.setbranch(label)
1356 repo.dirstate.setbranch(label)
1357 ui.status(_(b'marked working directory as branch %s\n') % label)
1357 ui.status(_(b'marked working directory as branch %s\n') % label)
1358
1358
1359 # find any open named branches aside from default
1359 # find any open named branches aside from default
1360 for n, h, t, c in repo.branchmap().iterbranches():
1360 for n, h, t, c in repo.branchmap().iterbranches():
1361 if n != b"default" and not c:
1361 if n != b"default" and not c:
1362 return 0
1362 return 0
1363 ui.status(
1363 ui.status(
1364 _(
1364 _(
1365 b'(branches are permanent and global, '
1365 b'(branches are permanent and global, '
1366 b'did you want a bookmark?)\n'
1366 b'did you want a bookmark?)\n'
1367 )
1367 )
1368 )
1368 )
1369
1369
1370
1370
1371 @command(
1371 @command(
1372 b'branches',
1372 b'branches',
1373 [
1373 [
1374 (
1374 (
1375 b'a',
1375 b'a',
1376 b'active',
1376 b'active',
1377 False,
1377 False,
1378 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1378 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1379 ),
1379 ),
1380 (b'c', b'closed', False, _(b'show normal and closed branches')),
1380 (b'c', b'closed', False, _(b'show normal and closed branches')),
1381 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1381 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1382 ]
1382 ]
1383 + formatteropts,
1383 + formatteropts,
1384 _(b'[-c]'),
1384 _(b'[-c]'),
1385 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1385 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1386 intents={INTENT_READONLY},
1386 intents={INTENT_READONLY},
1387 )
1387 )
1388 def branches(ui, repo, active=False, closed=False, **opts):
1388 def branches(ui, repo, active=False, closed=False, **opts):
1389 """list repository named branches
1389 """list repository named branches
1390
1390
1391 List the repository's named branches, indicating which ones are
1391 List the repository's named branches, indicating which ones are
1392 inactive. If -c/--closed is specified, also list branches which have
1392 inactive. If -c/--closed is specified, also list branches which have
1393 been marked closed (see :hg:`commit --close-branch`).
1393 been marked closed (see :hg:`commit --close-branch`).
1394
1394
1395 Use the command :hg:`update` to switch to an existing branch.
1395 Use the command :hg:`update` to switch to an existing branch.
1396
1396
1397 .. container:: verbose
1397 .. container:: verbose
1398
1398
1399 Template:
1399 Template:
1400
1400
1401 The following keywords are supported in addition to the common template
1401 The following keywords are supported in addition to the common template
1402 keywords and functions such as ``{branch}``. See also
1402 keywords and functions such as ``{branch}``. See also
1403 :hg:`help templates`.
1403 :hg:`help templates`.
1404
1404
1405 :active: Boolean. True if the branch is active.
1405 :active: Boolean. True if the branch is active.
1406 :closed: Boolean. True if the branch is closed.
1406 :closed: Boolean. True if the branch is closed.
1407 :current: Boolean. True if it is the current branch.
1407 :current: Boolean. True if it is the current branch.
1408
1408
1409 Returns 0.
1409 Returns 0.
1410 """
1410 """
1411
1411
1412 opts = pycompat.byteskwargs(opts)
1412 opts = pycompat.byteskwargs(opts)
1413 revs = opts.get(b'rev')
1413 revs = opts.get(b'rev')
1414 selectedbranches = None
1414 selectedbranches = None
1415 if revs:
1415 if revs:
1416 revs = scmutil.revrange(repo, revs)
1416 revs = scmutil.revrange(repo, revs)
1417 getbi = repo.revbranchcache().branchinfo
1417 getbi = repo.revbranchcache().branchinfo
1418 selectedbranches = {getbi(r)[0] for r in revs}
1418 selectedbranches = {getbi(r)[0] for r in revs}
1419
1419
1420 ui.pager(b'branches')
1420 ui.pager(b'branches')
1421 fm = ui.formatter(b'branches', opts)
1421 fm = ui.formatter(b'branches', opts)
1422 hexfunc = fm.hexfunc
1422 hexfunc = fm.hexfunc
1423
1423
1424 allheads = set(repo.heads())
1424 allheads = set(repo.heads())
1425 branches = []
1425 branches = []
1426 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1426 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1427 if selectedbranches is not None and tag not in selectedbranches:
1427 if selectedbranches is not None and tag not in selectedbranches:
1428 continue
1428 continue
1429 isactive = False
1429 isactive = False
1430 if not isclosed:
1430 if not isclosed:
1431 openheads = set(repo.branchmap().iteropen(heads))
1431 openheads = set(repo.branchmap().iteropen(heads))
1432 isactive = bool(openheads & allheads)
1432 isactive = bool(openheads & allheads)
1433 branches.append((tag, repo[tip], isactive, not isclosed))
1433 branches.append((tag, repo[tip], isactive, not isclosed))
1434 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1434 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1435
1435
1436 for tag, ctx, isactive, isopen in branches:
1436 for tag, ctx, isactive, isopen in branches:
1437 if active and not isactive:
1437 if active and not isactive:
1438 continue
1438 continue
1439 if isactive:
1439 if isactive:
1440 label = b'branches.active'
1440 label = b'branches.active'
1441 notice = b''
1441 notice = b''
1442 elif not isopen:
1442 elif not isopen:
1443 if not closed:
1443 if not closed:
1444 continue
1444 continue
1445 label = b'branches.closed'
1445 label = b'branches.closed'
1446 notice = _(b' (closed)')
1446 notice = _(b' (closed)')
1447 else:
1447 else:
1448 label = b'branches.inactive'
1448 label = b'branches.inactive'
1449 notice = _(b' (inactive)')
1449 notice = _(b' (inactive)')
1450 current = tag == repo.dirstate.branch()
1450 current = tag == repo.dirstate.branch()
1451 if current:
1451 if current:
1452 label = b'branches.current'
1452 label = b'branches.current'
1453
1453
1454 fm.startitem()
1454 fm.startitem()
1455 fm.write(b'branch', b'%s', tag, label=label)
1455 fm.write(b'branch', b'%s', tag, label=label)
1456 rev = ctx.rev()
1456 rev = ctx.rev()
1457 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1457 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1458 fmt = b' ' * padsize + b' %d:%s'
1458 fmt = b' ' * padsize + b' %d:%s'
1459 fm.condwrite(
1459 fm.condwrite(
1460 not ui.quiet,
1460 not ui.quiet,
1461 b'rev node',
1461 b'rev node',
1462 fmt,
1462 fmt,
1463 rev,
1463 rev,
1464 hexfunc(ctx.node()),
1464 hexfunc(ctx.node()),
1465 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1465 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1466 )
1466 )
1467 fm.context(ctx=ctx)
1467 fm.context(ctx=ctx)
1468 fm.data(active=isactive, closed=not isopen, current=current)
1468 fm.data(active=isactive, closed=not isopen, current=current)
1469 if not ui.quiet:
1469 if not ui.quiet:
1470 fm.plain(notice)
1470 fm.plain(notice)
1471 fm.plain(b'\n')
1471 fm.plain(b'\n')
1472 fm.end()
1472 fm.end()
1473
1473
1474
1474
1475 @command(
1475 @command(
1476 b'bundle',
1476 b'bundle',
1477 [
1477 [
1478 (
1478 (
1479 b'f',
1479 b'f',
1480 b'force',
1480 b'force',
1481 None,
1481 None,
1482 _(b'run even when the destination is unrelated'),
1482 _(b'run even when the destination is unrelated'),
1483 ),
1483 ),
1484 (
1484 (
1485 b'r',
1485 b'r',
1486 b'rev',
1486 b'rev',
1487 [],
1487 [],
1488 _(b'a changeset intended to be added to the destination'),
1488 _(b'a changeset intended to be added to the destination'),
1489 _(b'REV'),
1489 _(b'REV'),
1490 ),
1490 ),
1491 (
1491 (
1492 b'b',
1492 b'b',
1493 b'branch',
1493 b'branch',
1494 [],
1494 [],
1495 _(b'a specific branch you would like to bundle'),
1495 _(b'a specific branch you would like to bundle'),
1496 _(b'BRANCH'),
1496 _(b'BRANCH'),
1497 ),
1497 ),
1498 (
1498 (
1499 b'',
1499 b'',
1500 b'base',
1500 b'base',
1501 [],
1501 [],
1502 _(b'a base changeset assumed to be available at the destination'),
1502 _(b'a base changeset assumed to be available at the destination'),
1503 _(b'REV'),
1503 _(b'REV'),
1504 ),
1504 ),
1505 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1505 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1506 (
1506 (
1507 b't',
1507 b't',
1508 b'type',
1508 b'type',
1509 b'bzip2',
1509 b'bzip2',
1510 _(b'bundle compression type to use'),
1510 _(b'bundle compression type to use'),
1511 _(b'TYPE'),
1511 _(b'TYPE'),
1512 ),
1512 ),
1513 ]
1513 ]
1514 + remoteopts,
1514 + remoteopts,
1515 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1515 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1516 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1516 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1517 )
1517 )
1518 def bundle(ui, repo, fname, dest=None, **opts):
1518 def bundle(ui, repo, fname, dest=None, **opts):
1519 """create a bundle file
1519 """create a bundle file
1520
1520
1521 Generate a bundle file containing data to be transferred to another
1521 Generate a bundle file containing data to be transferred to another
1522 repository.
1522 repository.
1523
1523
1524 To create a bundle containing all changesets, use -a/--all
1524 To create a bundle containing all changesets, use -a/--all
1525 (or --base null). Otherwise, hg assumes the destination will have
1525 (or --base null). Otherwise, hg assumes the destination will have
1526 all the nodes you specify with --base parameters. Otherwise, hg
1526 all the nodes you specify with --base parameters. Otherwise, hg
1527 will assume the repository has all the nodes in destination, or
1527 will assume the repository has all the nodes in destination, or
1528 default-push/default if no destination is specified, where destination
1528 default-push/default if no destination is specified, where destination
1529 is the repository you provide through DEST option.
1529 is the repository you provide through DEST option.
1530
1530
1531 You can change bundle format with the -t/--type option. See
1531 You can change bundle format with the -t/--type option. See
1532 :hg:`help bundlespec` for documentation on this format. By default,
1532 :hg:`help bundlespec` for documentation on this format. By default,
1533 the most appropriate format is used and compression defaults to
1533 the most appropriate format is used and compression defaults to
1534 bzip2.
1534 bzip2.
1535
1535
1536 The bundle file can then be transferred using conventional means
1536 The bundle file can then be transferred using conventional means
1537 and applied to another repository with the unbundle or pull
1537 and applied to another repository with the unbundle or pull
1538 command. This is useful when direct push and pull are not
1538 command. This is useful when direct push and pull are not
1539 available or when exporting an entire repository is undesirable.
1539 available or when exporting an entire repository is undesirable.
1540
1540
1541 Applying bundles preserves all changeset contents including
1541 Applying bundles preserves all changeset contents including
1542 permissions, copy/rename information, and revision history.
1542 permissions, copy/rename information, and revision history.
1543
1543
1544 Returns 0 on success, 1 if no changes found.
1544 Returns 0 on success, 1 if no changes found.
1545 """
1545 """
1546 opts = pycompat.byteskwargs(opts)
1546 opts = pycompat.byteskwargs(opts)
1547 revs = None
1547 revs = None
1548 if b'rev' in opts:
1548 if b'rev' in opts:
1549 revstrings = opts[b'rev']
1549 revstrings = opts[b'rev']
1550 revs = scmutil.revrange(repo, revstrings)
1550 revs = scmutil.revrange(repo, revstrings)
1551 if revstrings and not revs:
1551 if revstrings and not revs:
1552 raise error.Abort(_(b'no commits to bundle'))
1552 raise error.Abort(_(b'no commits to bundle'))
1553
1553
1554 bundletype = opts.get(b'type', b'bzip2').lower()
1554 bundletype = opts.get(b'type', b'bzip2').lower()
1555 try:
1555 try:
1556 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1556 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1557 except error.UnsupportedBundleSpecification as e:
1557 except error.UnsupportedBundleSpecification as e:
1558 raise error.Abort(
1558 raise error.Abort(
1559 pycompat.bytestr(e),
1559 pycompat.bytestr(e),
1560 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1560 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1561 )
1561 )
1562 cgversion = bundlespec.contentopts[b"cg.version"]
1562 cgversion = bundlespec.contentopts[b"cg.version"]
1563
1563
1564 # Packed bundles are a pseudo bundle format for now.
1564 # Packed bundles are a pseudo bundle format for now.
1565 if cgversion == b's1':
1565 if cgversion == b's1':
1566 raise error.Abort(
1566 raise error.Abort(
1567 _(b'packed bundles cannot be produced by "hg bundle"'),
1567 _(b'packed bundles cannot be produced by "hg bundle"'),
1568 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1568 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1569 )
1569 )
1570
1570
1571 if opts.get(b'all'):
1571 if opts.get(b'all'):
1572 if dest:
1572 if dest:
1573 raise error.Abort(
1573 raise error.Abort(
1574 _(b"--all is incompatible with specifying a destination")
1574 _(b"--all is incompatible with specifying a destination")
1575 )
1575 )
1576 if opts.get(b'base'):
1576 if opts.get(b'base'):
1577 ui.warn(_(b"ignoring --base because --all was specified\n"))
1577 ui.warn(_(b"ignoring --base because --all was specified\n"))
1578 base = [nullrev]
1578 base = [nullrev]
1579 else:
1579 else:
1580 base = scmutil.revrange(repo, opts.get(b'base'))
1580 base = scmutil.revrange(repo, opts.get(b'base'))
1581 if cgversion not in changegroup.supportedoutgoingversions(repo):
1581 if cgversion not in changegroup.supportedoutgoingversions(repo):
1582 raise error.Abort(
1582 raise error.Abort(
1583 _(b"repository does not support bundle version %s") % cgversion
1583 _(b"repository does not support bundle version %s") % cgversion
1584 )
1584 )
1585
1585
1586 if base:
1586 if base:
1587 if dest:
1587 if dest:
1588 raise error.Abort(
1588 raise error.Abort(
1589 _(b"--base is incompatible with specifying a destination")
1589 _(b"--base is incompatible with specifying a destination")
1590 )
1590 )
1591 common = [repo[rev].node() for rev in base]
1591 common = [repo[rev].node() for rev in base]
1592 heads = [repo[r].node() for r in revs] if revs else None
1592 heads = [repo[r].node() for r in revs] if revs else None
1593 outgoing = discovery.outgoing(repo, common, heads)
1593 outgoing = discovery.outgoing(repo, common, heads)
1594 else:
1594 else:
1595 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1595 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1596 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1596 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1597 other = hg.peer(repo, opts, dest)
1597 other = hg.peer(repo, opts, dest)
1598 revs = [repo[r].hex() for r in revs]
1598 revs = [repo[r].hex() for r in revs]
1599 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1599 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1600 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1600 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1601 outgoing = discovery.findcommonoutgoing(
1601 outgoing = discovery.findcommonoutgoing(
1602 repo,
1602 repo,
1603 other,
1603 other,
1604 onlyheads=heads,
1604 onlyheads=heads,
1605 force=opts.get(b'force'),
1605 force=opts.get(b'force'),
1606 portable=True,
1606 portable=True,
1607 )
1607 )
1608
1608
1609 if not outgoing.missing:
1609 if not outgoing.missing:
1610 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1610 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1611 return 1
1611 return 1
1612
1612
1613 if cgversion == b'01': # bundle1
1613 if cgversion == b'01': # bundle1
1614 bversion = b'HG10' + bundlespec.wirecompression
1614 bversion = b'HG10' + bundlespec.wirecompression
1615 bcompression = None
1615 bcompression = None
1616 elif cgversion in (b'02', b'03'):
1616 elif cgversion in (b'02', b'03'):
1617 bversion = b'HG20'
1617 bversion = b'HG20'
1618 bcompression = bundlespec.wirecompression
1618 bcompression = bundlespec.wirecompression
1619 else:
1619 else:
1620 raise error.ProgrammingError(
1620 raise error.ProgrammingError(
1621 b'bundle: unexpected changegroup version %s' % cgversion
1621 b'bundle: unexpected changegroup version %s' % cgversion
1622 )
1622 )
1623
1623
1624 # TODO compression options should be derived from bundlespec parsing.
1624 # TODO compression options should be derived from bundlespec parsing.
1625 # This is a temporary hack to allow adjusting bundle compression
1625 # This is a temporary hack to allow adjusting bundle compression
1626 # level without a) formalizing the bundlespec changes to declare it
1626 # level without a) formalizing the bundlespec changes to declare it
1627 # b) introducing a command flag.
1627 # b) introducing a command flag.
1628 compopts = {}
1628 compopts = {}
1629 complevel = ui.configint(
1629 complevel = ui.configint(
1630 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1630 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1631 )
1631 )
1632 if complevel is None:
1632 if complevel is None:
1633 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1633 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1634 if complevel is not None:
1634 if complevel is not None:
1635 compopts[b'level'] = complevel
1635 compopts[b'level'] = complevel
1636
1636
1637 # Allow overriding the bundling of obsmarker in phases through
1637 # Allow overriding the bundling of obsmarker in phases through
1638 # configuration while we don't have a bundle version that include them
1638 # configuration while we don't have a bundle version that include them
1639 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1639 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1640 bundlespec.contentopts[b'obsolescence'] = True
1640 bundlespec.contentopts[b'obsolescence'] = True
1641 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1641 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1642 bundlespec.contentopts[b'phases'] = True
1642 bundlespec.contentopts[b'phases'] = True
1643
1643
1644 bundle2.writenewbundle(
1644 bundle2.writenewbundle(
1645 ui,
1645 ui,
1646 repo,
1646 repo,
1647 b'bundle',
1647 b'bundle',
1648 fname,
1648 fname,
1649 bversion,
1649 bversion,
1650 outgoing,
1650 outgoing,
1651 bundlespec.contentopts,
1651 bundlespec.contentopts,
1652 compression=bcompression,
1652 compression=bcompression,
1653 compopts=compopts,
1653 compopts=compopts,
1654 )
1654 )
1655
1655
1656
1656
1657 @command(
1657 @command(
1658 b'cat',
1658 b'cat',
1659 [
1659 [
1660 (
1660 (
1661 b'o',
1661 b'o',
1662 b'output',
1662 b'output',
1663 b'',
1663 b'',
1664 _(b'print output to file with formatted name'),
1664 _(b'print output to file with formatted name'),
1665 _(b'FORMAT'),
1665 _(b'FORMAT'),
1666 ),
1666 ),
1667 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1667 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1668 (b'', b'decode', None, _(b'apply any matching decode filter')),
1668 (b'', b'decode', None, _(b'apply any matching decode filter')),
1669 ]
1669 ]
1670 + walkopts
1670 + walkopts
1671 + formatteropts,
1671 + formatteropts,
1672 _(b'[OPTION]... FILE...'),
1672 _(b'[OPTION]... FILE...'),
1673 helpcategory=command.CATEGORY_FILE_CONTENTS,
1673 helpcategory=command.CATEGORY_FILE_CONTENTS,
1674 inferrepo=True,
1674 inferrepo=True,
1675 intents={INTENT_READONLY},
1675 intents={INTENT_READONLY},
1676 )
1676 )
1677 def cat(ui, repo, file1, *pats, **opts):
1677 def cat(ui, repo, file1, *pats, **opts):
1678 """output the current or given revision of files
1678 """output the current or given revision of files
1679
1679
1680 Print the specified files as they were at the given revision. If
1680 Print the specified files as they were at the given revision. If
1681 no revision is given, the parent of the working directory is used.
1681 no revision is given, the parent of the working directory is used.
1682
1682
1683 Output may be to a file, in which case the name of the file is
1683 Output may be to a file, in which case the name of the file is
1684 given using a template string. See :hg:`help templates`. In addition
1684 given using a template string. See :hg:`help templates`. In addition
1685 to the common template keywords, the following formatting rules are
1685 to the common template keywords, the following formatting rules are
1686 supported:
1686 supported:
1687
1687
1688 :``%%``: literal "%" character
1688 :``%%``: literal "%" character
1689 :``%s``: basename of file being printed
1689 :``%s``: basename of file being printed
1690 :``%d``: dirname of file being printed, or '.' if in repository root
1690 :``%d``: dirname of file being printed, or '.' if in repository root
1691 :``%p``: root-relative path name of file being printed
1691 :``%p``: root-relative path name of file being printed
1692 :``%H``: changeset hash (40 hexadecimal digits)
1692 :``%H``: changeset hash (40 hexadecimal digits)
1693 :``%R``: changeset revision number
1693 :``%R``: changeset revision number
1694 :``%h``: short-form changeset hash (12 hexadecimal digits)
1694 :``%h``: short-form changeset hash (12 hexadecimal digits)
1695 :``%r``: zero-padded changeset revision number
1695 :``%r``: zero-padded changeset revision number
1696 :``%b``: basename of the exporting repository
1696 :``%b``: basename of the exporting repository
1697 :``\\``: literal "\\" character
1697 :``\\``: literal "\\" character
1698
1698
1699 .. container:: verbose
1699 .. container:: verbose
1700
1700
1701 Template:
1701 Template:
1702
1702
1703 The following keywords are supported in addition to the common template
1703 The following keywords are supported in addition to the common template
1704 keywords and functions. See also :hg:`help templates`.
1704 keywords and functions. See also :hg:`help templates`.
1705
1705
1706 :data: String. File content.
1706 :data: String. File content.
1707 :path: String. Repository-absolute path of the file.
1707 :path: String. Repository-absolute path of the file.
1708
1708
1709 Returns 0 on success.
1709 Returns 0 on success.
1710 """
1710 """
1711 opts = pycompat.byteskwargs(opts)
1711 opts = pycompat.byteskwargs(opts)
1712 rev = opts.get(b'rev')
1712 rev = opts.get(b'rev')
1713 if rev:
1713 if rev:
1714 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1714 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1715 ctx = scmutil.revsingle(repo, rev)
1715 ctx = scmutil.revsingle(repo, rev)
1716 m = scmutil.match(ctx, (file1,) + pats, opts)
1716 m = scmutil.match(ctx, (file1,) + pats, opts)
1717 fntemplate = opts.pop(b'output', b'')
1717 fntemplate = opts.pop(b'output', b'')
1718 if cmdutil.isstdiofilename(fntemplate):
1718 if cmdutil.isstdiofilename(fntemplate):
1719 fntemplate = b''
1719 fntemplate = b''
1720
1720
1721 if fntemplate:
1721 if fntemplate:
1722 fm = formatter.nullformatter(ui, b'cat', opts)
1722 fm = formatter.nullformatter(ui, b'cat', opts)
1723 else:
1723 else:
1724 ui.pager(b'cat')
1724 ui.pager(b'cat')
1725 fm = ui.formatter(b'cat', opts)
1725 fm = ui.formatter(b'cat', opts)
1726 with fm:
1726 with fm:
1727 return cmdutil.cat(
1727 return cmdutil.cat(
1728 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1728 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1729 )
1729 )
1730
1730
1731
1731
1732 @command(
1732 @command(
1733 b'clone',
1733 b'clone',
1734 [
1734 [
1735 (
1735 (
1736 b'U',
1736 b'U',
1737 b'noupdate',
1737 b'noupdate',
1738 None,
1738 None,
1739 _(
1739 _(
1740 b'the clone will include an empty working '
1740 b'the clone will include an empty working '
1741 b'directory (only a repository)'
1741 b'directory (only a repository)'
1742 ),
1742 ),
1743 ),
1743 ),
1744 (
1744 (
1745 b'u',
1745 b'u',
1746 b'updaterev',
1746 b'updaterev',
1747 b'',
1747 b'',
1748 _(b'revision, tag, or branch to check out'),
1748 _(b'revision, tag, or branch to check out'),
1749 _(b'REV'),
1749 _(b'REV'),
1750 ),
1750 ),
1751 (
1751 (
1752 b'r',
1752 b'r',
1753 b'rev',
1753 b'rev',
1754 [],
1754 [],
1755 _(
1755 _(
1756 b'do not clone everything, but include this changeset'
1756 b'do not clone everything, but include this changeset'
1757 b' and its ancestors'
1757 b' and its ancestors'
1758 ),
1758 ),
1759 _(b'REV'),
1759 _(b'REV'),
1760 ),
1760 ),
1761 (
1761 (
1762 b'b',
1762 b'b',
1763 b'branch',
1763 b'branch',
1764 [],
1764 [],
1765 _(
1765 _(
1766 b'do not clone everything, but include this branch\'s'
1766 b'do not clone everything, but include this branch\'s'
1767 b' changesets and their ancestors'
1767 b' changesets and their ancestors'
1768 ),
1768 ),
1769 _(b'BRANCH'),
1769 _(b'BRANCH'),
1770 ),
1770 ),
1771 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1771 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1772 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1772 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1773 (b'', b'stream', None, _(b'clone with minimal data processing')),
1773 (b'', b'stream', None, _(b'clone with minimal data processing')),
1774 ]
1774 ]
1775 + remoteopts,
1775 + remoteopts,
1776 _(b'[OPTION]... SOURCE [DEST]'),
1776 _(b'[OPTION]... SOURCE [DEST]'),
1777 helpcategory=command.CATEGORY_REPO_CREATION,
1777 helpcategory=command.CATEGORY_REPO_CREATION,
1778 helpbasic=True,
1778 helpbasic=True,
1779 norepo=True,
1779 norepo=True,
1780 )
1780 )
1781 def clone(ui, source, dest=None, **opts):
1781 def clone(ui, source, dest=None, **opts):
1782 """make a copy of an existing repository
1782 """make a copy of an existing repository
1783
1783
1784 Create a copy of an existing repository in a new directory.
1784 Create a copy of an existing repository in a new directory.
1785
1785
1786 If no destination directory name is specified, it defaults to the
1786 If no destination directory name is specified, it defaults to the
1787 basename of the source.
1787 basename of the source.
1788
1788
1789 The location of the source is added to the new repository's
1789 The location of the source is added to the new repository's
1790 ``.hg/hgrc`` file, as the default to be used for future pulls.
1790 ``.hg/hgrc`` file, as the default to be used for future pulls.
1791
1791
1792 Only local paths and ``ssh://`` URLs are supported as
1792 Only local paths and ``ssh://`` URLs are supported as
1793 destinations. For ``ssh://`` destinations, no working directory or
1793 destinations. For ``ssh://`` destinations, no working directory or
1794 ``.hg/hgrc`` will be created on the remote side.
1794 ``.hg/hgrc`` will be created on the remote side.
1795
1795
1796 If the source repository has a bookmark called '@' set, that
1796 If the source repository has a bookmark called '@' set, that
1797 revision will be checked out in the new repository by default.
1797 revision will be checked out in the new repository by default.
1798
1798
1799 To check out a particular version, use -u/--update, or
1799 To check out a particular version, use -u/--update, or
1800 -U/--noupdate to create a clone with no working directory.
1800 -U/--noupdate to create a clone with no working directory.
1801
1801
1802 To pull only a subset of changesets, specify one or more revisions
1802 To pull only a subset of changesets, specify one or more revisions
1803 identifiers with -r/--rev or branches with -b/--branch. The
1803 identifiers with -r/--rev or branches with -b/--branch. The
1804 resulting clone will contain only the specified changesets and
1804 resulting clone will contain only the specified changesets and
1805 their ancestors. These options (or 'clone src#rev dest') imply
1805 their ancestors. These options (or 'clone src#rev dest') imply
1806 --pull, even for local source repositories.
1806 --pull, even for local source repositories.
1807
1807
1808 In normal clone mode, the remote normalizes repository data into a common
1808 In normal clone mode, the remote normalizes repository data into a common
1809 exchange format and the receiving end translates this data into its local
1809 exchange format and the receiving end translates this data into its local
1810 storage format. --stream activates a different clone mode that essentially
1810 storage format. --stream activates a different clone mode that essentially
1811 copies repository files from the remote with minimal data processing. This
1811 copies repository files from the remote with minimal data processing. This
1812 significantly reduces the CPU cost of a clone both remotely and locally.
1812 significantly reduces the CPU cost of a clone both remotely and locally.
1813 However, it often increases the transferred data size by 30-40%. This can
1813 However, it often increases the transferred data size by 30-40%. This can
1814 result in substantially faster clones where I/O throughput is plentiful,
1814 result in substantially faster clones where I/O throughput is plentiful,
1815 especially for larger repositories. A side-effect of --stream clones is
1815 especially for larger repositories. A side-effect of --stream clones is
1816 that storage settings and requirements on the remote are applied locally:
1816 that storage settings and requirements on the remote are applied locally:
1817 a modern client may inherit legacy or inefficient storage used by the
1817 a modern client may inherit legacy or inefficient storage used by the
1818 remote or a legacy Mercurial client may not be able to clone from a
1818 remote or a legacy Mercurial client may not be able to clone from a
1819 modern Mercurial remote.
1819 modern Mercurial remote.
1820
1820
1821 .. note::
1821 .. note::
1822
1822
1823 Specifying a tag will include the tagged changeset but not the
1823 Specifying a tag will include the tagged changeset but not the
1824 changeset containing the tag.
1824 changeset containing the tag.
1825
1825
1826 .. container:: verbose
1826 .. container:: verbose
1827
1827
1828 For efficiency, hardlinks are used for cloning whenever the
1828 For efficiency, hardlinks are used for cloning whenever the
1829 source and destination are on the same filesystem (note this
1829 source and destination are on the same filesystem (note this
1830 applies only to the repository data, not to the working
1830 applies only to the repository data, not to the working
1831 directory). Some filesystems, such as AFS, implement hardlinking
1831 directory). Some filesystems, such as AFS, implement hardlinking
1832 incorrectly, but do not report errors. In these cases, use the
1832 incorrectly, but do not report errors. In these cases, use the
1833 --pull option to avoid hardlinking.
1833 --pull option to avoid hardlinking.
1834
1834
1835 Mercurial will update the working directory to the first applicable
1835 Mercurial will update the working directory to the first applicable
1836 revision from this list:
1836 revision from this list:
1837
1837
1838 a) null if -U or the source repository has no changesets
1838 a) null if -U or the source repository has no changesets
1839 b) if -u . and the source repository is local, the first parent of
1839 b) if -u . and the source repository is local, the first parent of
1840 the source repository's working directory
1840 the source repository's working directory
1841 c) the changeset specified with -u (if a branch name, this means the
1841 c) the changeset specified with -u (if a branch name, this means the
1842 latest head of that branch)
1842 latest head of that branch)
1843 d) the changeset specified with -r
1843 d) the changeset specified with -r
1844 e) the tipmost head specified with -b
1844 e) the tipmost head specified with -b
1845 f) the tipmost head specified with the url#branch source syntax
1845 f) the tipmost head specified with the url#branch source syntax
1846 g) the revision marked with the '@' bookmark, if present
1846 g) the revision marked with the '@' bookmark, if present
1847 h) the tipmost head of the default branch
1847 h) the tipmost head of the default branch
1848 i) tip
1848 i) tip
1849
1849
1850 When cloning from servers that support it, Mercurial may fetch
1850 When cloning from servers that support it, Mercurial may fetch
1851 pre-generated data from a server-advertised URL or inline from the
1851 pre-generated data from a server-advertised URL or inline from the
1852 same stream. When this is done, hooks operating on incoming changesets
1852 same stream. When this is done, hooks operating on incoming changesets
1853 and changegroups may fire more than once, once for each pre-generated
1853 and changegroups may fire more than once, once for each pre-generated
1854 bundle and as well as for any additional remaining data. In addition,
1854 bundle and as well as for any additional remaining data. In addition,
1855 if an error occurs, the repository may be rolled back to a partial
1855 if an error occurs, the repository may be rolled back to a partial
1856 clone. This behavior may change in future releases.
1856 clone. This behavior may change in future releases.
1857 See :hg:`help -e clonebundles` for more.
1857 See :hg:`help -e clonebundles` for more.
1858
1858
1859 Examples:
1859 Examples:
1860
1860
1861 - clone a remote repository to a new directory named hg/::
1861 - clone a remote repository to a new directory named hg/::
1862
1862
1863 hg clone https://www.mercurial-scm.org/repo/hg/
1863 hg clone https://www.mercurial-scm.org/repo/hg/
1864
1864
1865 - create a lightweight local clone::
1865 - create a lightweight local clone::
1866
1866
1867 hg clone project/ project-feature/
1867 hg clone project/ project-feature/
1868
1868
1869 - clone from an absolute path on an ssh server (note double-slash)::
1869 - clone from an absolute path on an ssh server (note double-slash)::
1870
1870
1871 hg clone ssh://user@server//home/projects/alpha/
1871 hg clone ssh://user@server//home/projects/alpha/
1872
1872
1873 - do a streaming clone while checking out a specified version::
1873 - do a streaming clone while checking out a specified version::
1874
1874
1875 hg clone --stream http://server/repo -u 1.5
1875 hg clone --stream http://server/repo -u 1.5
1876
1876
1877 - create a repository without changesets after a particular revision::
1877 - create a repository without changesets after a particular revision::
1878
1878
1879 hg clone -r 04e544 experimental/ good/
1879 hg clone -r 04e544 experimental/ good/
1880
1880
1881 - clone (and track) a particular named branch::
1881 - clone (and track) a particular named branch::
1882
1882
1883 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1883 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1884
1884
1885 See :hg:`help urls` for details on specifying URLs.
1885 See :hg:`help urls` for details on specifying URLs.
1886
1886
1887 Returns 0 on success.
1887 Returns 0 on success.
1888 """
1888 """
1889 opts = pycompat.byteskwargs(opts)
1889 opts = pycompat.byteskwargs(opts)
1890 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1890 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1891
1891
1892 # --include/--exclude can come from narrow or sparse.
1892 # --include/--exclude can come from narrow or sparse.
1893 includepats, excludepats = None, None
1893 includepats, excludepats = None, None
1894
1894
1895 # hg.clone() differentiates between None and an empty set. So make sure
1895 # hg.clone() differentiates between None and an empty set. So make sure
1896 # patterns are sets if narrow is requested without patterns.
1896 # patterns are sets if narrow is requested without patterns.
1897 if opts.get(b'narrow'):
1897 if opts.get(b'narrow'):
1898 includepats = set()
1898 includepats = set()
1899 excludepats = set()
1899 excludepats = set()
1900
1900
1901 if opts.get(b'include'):
1901 if opts.get(b'include'):
1902 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1902 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1903 if opts.get(b'exclude'):
1903 if opts.get(b'exclude'):
1904 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1904 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1905
1905
1906 r = hg.clone(
1906 r = hg.clone(
1907 ui,
1907 ui,
1908 opts,
1908 opts,
1909 source,
1909 source,
1910 dest,
1910 dest,
1911 pull=opts.get(b'pull'),
1911 pull=opts.get(b'pull'),
1912 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1912 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1913 revs=opts.get(b'rev'),
1913 revs=opts.get(b'rev'),
1914 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1914 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1915 branch=opts.get(b'branch'),
1915 branch=opts.get(b'branch'),
1916 shareopts=opts.get(b'shareopts'),
1916 shareopts=opts.get(b'shareopts'),
1917 storeincludepats=includepats,
1917 storeincludepats=includepats,
1918 storeexcludepats=excludepats,
1918 storeexcludepats=excludepats,
1919 depth=opts.get(b'depth') or None,
1919 depth=opts.get(b'depth') or None,
1920 )
1920 )
1921
1921
1922 return r is None
1922 return r is None
1923
1923
1924
1924
1925 @command(
1925 @command(
1926 b'commit|ci',
1926 b'commit|ci',
1927 [
1927 [
1928 (
1928 (
1929 b'A',
1929 b'A',
1930 b'addremove',
1930 b'addremove',
1931 None,
1931 None,
1932 _(b'mark new/missing files as added/removed before committing'),
1932 _(b'mark new/missing files as added/removed before committing'),
1933 ),
1933 ),
1934 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1934 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1935 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1935 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1936 (b's', b'secret', None, _(b'use the secret phase for committing')),
1936 (b's', b'secret', None, _(b'use the secret phase for committing')),
1937 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1937 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1938 (
1938 (
1939 b'',
1939 b'',
1940 b'force-close-branch',
1940 b'force-close-branch',
1941 None,
1941 None,
1942 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1942 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1943 ),
1943 ),
1944 (b'i', b'interactive', None, _(b'use interactive mode')),
1944 (b'i', b'interactive', None, _(b'use interactive mode')),
1945 ]
1945 ]
1946 + walkopts
1946 + walkopts
1947 + commitopts
1947 + commitopts
1948 + commitopts2
1948 + commitopts2
1949 + subrepoopts,
1949 + subrepoopts,
1950 _(b'[OPTION]... [FILE]...'),
1950 _(b'[OPTION]... [FILE]...'),
1951 helpcategory=command.CATEGORY_COMMITTING,
1951 helpcategory=command.CATEGORY_COMMITTING,
1952 helpbasic=True,
1952 helpbasic=True,
1953 inferrepo=True,
1953 inferrepo=True,
1954 )
1954 )
1955 def commit(ui, repo, *pats, **opts):
1955 def commit(ui, repo, *pats, **opts):
1956 """commit the specified files or all outstanding changes
1956 """commit the specified files or all outstanding changes
1957
1957
1958 Commit changes to the given files into the repository. Unlike a
1958 Commit changes to the given files into the repository. Unlike a
1959 centralized SCM, this operation is a local operation. See
1959 centralized SCM, this operation is a local operation. See
1960 :hg:`push` for a way to actively distribute your changes.
1960 :hg:`push` for a way to actively distribute your changes.
1961
1961
1962 If a list of files is omitted, all changes reported by :hg:`status`
1962 If a list of files is omitted, all changes reported by :hg:`status`
1963 will be committed.
1963 will be committed.
1964
1964
1965 If you are committing the result of a merge, do not provide any
1965 If you are committing the result of a merge, do not provide any
1966 filenames or -I/-X filters.
1966 filenames or -I/-X filters.
1967
1967
1968 If no commit message is specified, Mercurial starts your
1968 If no commit message is specified, Mercurial starts your
1969 configured editor where you can enter a message. In case your
1969 configured editor where you can enter a message. In case your
1970 commit fails, you will find a backup of your message in
1970 commit fails, you will find a backup of your message in
1971 ``.hg/last-message.txt``.
1971 ``.hg/last-message.txt``.
1972
1972
1973 The --close-branch flag can be used to mark the current branch
1973 The --close-branch flag can be used to mark the current branch
1974 head closed. When all heads of a branch are closed, the branch
1974 head closed. When all heads of a branch are closed, the branch
1975 will be considered closed and no longer listed.
1975 will be considered closed and no longer listed.
1976
1976
1977 The --amend flag can be used to amend the parent of the
1977 The --amend flag can be used to amend the parent of the
1978 working directory with a new commit that contains the changes
1978 working directory with a new commit that contains the changes
1979 in the parent in addition to those currently reported by :hg:`status`,
1979 in the parent in addition to those currently reported by :hg:`status`,
1980 if there are any. The old commit is stored in a backup bundle in
1980 if there are any. The old commit is stored in a backup bundle in
1981 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1981 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1982 on how to restore it).
1982 on how to restore it).
1983
1983
1984 Message, user and date are taken from the amended commit unless
1984 Message, user and date are taken from the amended commit unless
1985 specified. When a message isn't specified on the command line,
1985 specified. When a message isn't specified on the command line,
1986 the editor will open with the message of the amended commit.
1986 the editor will open with the message of the amended commit.
1987
1987
1988 It is not possible to amend public changesets (see :hg:`help phases`)
1988 It is not possible to amend public changesets (see :hg:`help phases`)
1989 or changesets that have children.
1989 or changesets that have children.
1990
1990
1991 See :hg:`help dates` for a list of formats valid for -d/--date.
1991 See :hg:`help dates` for a list of formats valid for -d/--date.
1992
1992
1993 Returns 0 on success, 1 if nothing changed.
1993 Returns 0 on success, 1 if nothing changed.
1994
1994
1995 .. container:: verbose
1995 .. container:: verbose
1996
1996
1997 Examples:
1997 Examples:
1998
1998
1999 - commit all files ending in .py::
1999 - commit all files ending in .py::
2000
2000
2001 hg commit --include "set:**.py"
2001 hg commit --include "set:**.py"
2002
2002
2003 - commit all non-binary files::
2003 - commit all non-binary files::
2004
2004
2005 hg commit --exclude "set:binary()"
2005 hg commit --exclude "set:binary()"
2006
2006
2007 - amend the current commit and set the date to now::
2007 - amend the current commit and set the date to now::
2008
2008
2009 hg commit --amend --date now
2009 hg commit --amend --date now
2010 """
2010 """
2011 with repo.wlock(), repo.lock():
2011 with repo.wlock(), repo.lock():
2012 return _docommit(ui, repo, *pats, **opts)
2012 return _docommit(ui, repo, *pats, **opts)
2013
2013
2014
2014
2015 def _docommit(ui, repo, *pats, **opts):
2015 def _docommit(ui, repo, *pats, **opts):
2016 if opts.get('interactive'):
2016 if opts.get('interactive'):
2017 opts.pop('interactive')
2017 opts.pop('interactive')
2018 ret = cmdutil.dorecord(
2018 ret = cmdutil.dorecord(
2019 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2019 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2020 )
2020 )
2021 # ret can be 0 (no changes to record) or the value returned by
2021 # ret can be 0 (no changes to record) or the value returned by
2022 # commit(), 1 if nothing changed or None on success.
2022 # commit(), 1 if nothing changed or None on success.
2023 return 1 if ret == 0 else ret
2023 return 1 if ret == 0 else ret
2024
2024
2025 opts = pycompat.byteskwargs(opts)
2025 opts = pycompat.byteskwargs(opts)
2026 if opts.get(b'subrepos'):
2026 if opts.get(b'subrepos'):
2027 if opts.get(b'amend'):
2027 if opts.get(b'amend'):
2028 raise error.Abort(_(b'cannot amend with --subrepos'))
2028 raise error.Abort(_(b'cannot amend with --subrepos'))
2029 # Let --subrepos on the command line override config setting.
2029 # Let --subrepos on the command line override config setting.
2030 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2030 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2031
2031
2032 cmdutil.checkunfinished(repo, commit=True)
2032 cmdutil.checkunfinished(repo, commit=True)
2033
2033
2034 branch = repo[None].branch()
2034 branch = repo[None].branch()
2035 bheads = repo.branchheads(branch)
2035 bheads = repo.branchheads(branch)
2036
2036
2037 extra = {}
2037 extra = {}
2038 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2038 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2039 extra[b'close'] = b'1'
2039 extra[b'close'] = b'1'
2040
2040
2041 if repo[b'.'].closesbranch():
2041 if repo[b'.'].closesbranch():
2042 raise error.Abort(
2042 raise error.Abort(
2043 _(b'current revision is already a branch closing head')
2043 _(b'current revision is already a branch closing head')
2044 )
2044 )
2045 elif not bheads:
2045 elif not bheads:
2046 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2046 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2047 elif (
2047 elif (
2048 branch == repo[b'.'].branch()
2048 branch == repo[b'.'].branch()
2049 and repo[b'.'].node() not in bheads
2049 and repo[b'.'].node() not in bheads
2050 and not opts.get(b'force_close_branch')
2050 and not opts.get(b'force_close_branch')
2051 ):
2051 ):
2052 hint = _(
2052 hint = _(
2053 b'use --force-close-branch to close branch from a non-head'
2053 b'use --force-close-branch to close branch from a non-head'
2054 b' changeset'
2054 b' changeset'
2055 )
2055 )
2056 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2056 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2057 elif opts.get(b'amend'):
2057 elif opts.get(b'amend'):
2058 if (
2058 if (
2059 repo[b'.'].p1().branch() != branch
2059 repo[b'.'].p1().branch() != branch
2060 and repo[b'.'].p2().branch() != branch
2060 and repo[b'.'].p2().branch() != branch
2061 ):
2061 ):
2062 raise error.Abort(_(b'can only close branch heads'))
2062 raise error.Abort(_(b'can only close branch heads'))
2063
2063
2064 if opts.get(b'amend'):
2064 if opts.get(b'amend'):
2065 if ui.configbool(b'ui', b'commitsubrepos'):
2065 if ui.configbool(b'ui', b'commitsubrepos'):
2066 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2066 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2067
2067
2068 old = repo[b'.']
2068 old = repo[b'.']
2069 rewriteutil.precheck(repo, [old.rev()], b'amend')
2069 rewriteutil.precheck(repo, [old.rev()], b'amend')
2070
2070
2071 # Currently histedit gets confused if an amend happens while histedit
2071 # Currently histedit gets confused if an amend happens while histedit
2072 # is in progress. Since we have a checkunfinished command, we are
2072 # is in progress. Since we have a checkunfinished command, we are
2073 # temporarily honoring it.
2073 # temporarily honoring it.
2074 #
2074 #
2075 # Note: eventually this guard will be removed. Please do not expect
2075 # Note: eventually this guard will be removed. Please do not expect
2076 # this behavior to remain.
2076 # this behavior to remain.
2077 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2077 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2078 cmdutil.checkunfinished(repo)
2078 cmdutil.checkunfinished(repo)
2079
2079
2080 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2080 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2081 if node == old.node():
2081 if node == old.node():
2082 ui.status(_(b"nothing changed\n"))
2082 ui.status(_(b"nothing changed\n"))
2083 return 1
2083 return 1
2084 else:
2084 else:
2085
2085
2086 def commitfunc(ui, repo, message, match, opts):
2086 def commitfunc(ui, repo, message, match, opts):
2087 overrides = {}
2087 overrides = {}
2088 if opts.get(b'secret'):
2088 if opts.get(b'secret'):
2089 overrides[(b'phases', b'new-commit')] = b'secret'
2089 overrides[(b'phases', b'new-commit')] = b'secret'
2090
2090
2091 baseui = repo.baseui
2091 baseui = repo.baseui
2092 with baseui.configoverride(overrides, b'commit'):
2092 with baseui.configoverride(overrides, b'commit'):
2093 with ui.configoverride(overrides, b'commit'):
2093 with ui.configoverride(overrides, b'commit'):
2094 editform = cmdutil.mergeeditform(
2094 editform = cmdutil.mergeeditform(
2095 repo[None], b'commit.normal'
2095 repo[None], b'commit.normal'
2096 )
2096 )
2097 editor = cmdutil.getcommiteditor(
2097 editor = cmdutil.getcommiteditor(
2098 editform=editform, **pycompat.strkwargs(opts)
2098 editform=editform, **pycompat.strkwargs(opts)
2099 )
2099 )
2100 return repo.commit(
2100 return repo.commit(
2101 message,
2101 message,
2102 opts.get(b'user'),
2102 opts.get(b'user'),
2103 opts.get(b'date'),
2103 opts.get(b'date'),
2104 match,
2104 match,
2105 editor=editor,
2105 editor=editor,
2106 extra=extra,
2106 extra=extra,
2107 )
2107 )
2108
2108
2109 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2109 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2110
2110
2111 if not node:
2111 if not node:
2112 stat = cmdutil.postcommitstatus(repo, pats, opts)
2112 stat = cmdutil.postcommitstatus(repo, pats, opts)
2113 if stat.deleted:
2113 if stat.deleted:
2114 ui.status(
2114 ui.status(
2115 _(
2115 _(
2116 b"nothing changed (%d missing files, see "
2116 b"nothing changed (%d missing files, see "
2117 b"'hg status')\n"
2117 b"'hg status')\n"
2118 )
2118 )
2119 % len(stat.deleted)
2119 % len(stat.deleted)
2120 )
2120 )
2121 else:
2121 else:
2122 ui.status(_(b"nothing changed\n"))
2122 ui.status(_(b"nothing changed\n"))
2123 return 1
2123 return 1
2124
2124
2125 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2125 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2126
2126
2127 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2127 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2128 status(
2128 status(
2129 ui,
2129 ui,
2130 repo,
2130 repo,
2131 modified=True,
2131 modified=True,
2132 added=True,
2132 added=True,
2133 removed=True,
2133 removed=True,
2134 deleted=True,
2134 deleted=True,
2135 unknown=True,
2135 unknown=True,
2136 subrepos=opts.get(b'subrepos'),
2136 subrepos=opts.get(b'subrepos'),
2137 )
2137 )
2138
2138
2139
2139
2140 @command(
2140 @command(
2141 b'config|showconfig|debugconfig',
2141 b'config|showconfig|debugconfig',
2142 [
2142 [
2143 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2143 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2144 (b'e', b'edit', None, _(b'edit user config')),
2144 (b'e', b'edit', None, _(b'edit user config')),
2145 (b'l', b'local', None, _(b'edit repository config')),
2145 (b'l', b'local', None, _(b'edit repository config')),
2146 (
2146 (
2147 b'',
2147 b'',
2148 b'shared',
2148 b'shared',
2149 None,
2149 None,
2150 _(b'edit shared source repository config (EXPERIMENTAL)'),
2150 _(b'edit shared source repository config (EXPERIMENTAL)'),
2151 ),
2151 ),
2152 (b'g', b'global', None, _(b'edit global config')),
2152 (b'g', b'global', None, _(b'edit global config')),
2153 ]
2153 ]
2154 + formatteropts,
2154 + formatteropts,
2155 _(b'[-u] [NAME]...'),
2155 _(b'[-u] [NAME]...'),
2156 helpcategory=command.CATEGORY_HELP,
2156 helpcategory=command.CATEGORY_HELP,
2157 optionalrepo=True,
2157 optionalrepo=True,
2158 intents={INTENT_READONLY},
2158 intents={INTENT_READONLY},
2159 )
2159 )
2160 def config(ui, repo, *values, **opts):
2160 def config(ui, repo, *values, **opts):
2161 """show combined config settings from all hgrc files
2161 """show combined config settings from all hgrc files
2162
2162
2163 With no arguments, print names and values of all config items.
2163 With no arguments, print names and values of all config items.
2164
2164
2165 With one argument of the form section.name, print just the value
2165 With one argument of the form section.name, print just the value
2166 of that config item.
2166 of that config item.
2167
2167
2168 With multiple arguments, print names and values of all config
2168 With multiple arguments, print names and values of all config
2169 items with matching section names or section.names.
2169 items with matching section names or section.names.
2170
2170
2171 With --edit, start an editor on the user-level config file. With
2171 With --edit, start an editor on the user-level config file. With
2172 --global, edit the system-wide config file. With --local, edit the
2172 --global, edit the system-wide config file. With --local, edit the
2173 repository-level config file.
2173 repository-level config file.
2174
2174
2175 With --debug, the source (filename and line number) is printed
2175 With --debug, the source (filename and line number) is printed
2176 for each config item.
2176 for each config item.
2177
2177
2178 See :hg:`help config` for more information about config files.
2178 See :hg:`help config` for more information about config files.
2179
2179
2180 .. container:: verbose
2180 .. container:: verbose
2181
2181
2182 Template:
2182 Template:
2183
2183
2184 The following keywords are supported. See also :hg:`help templates`.
2184 The following keywords are supported. See also :hg:`help templates`.
2185
2185
2186 :name: String. Config name.
2186 :name: String. Config name.
2187 :source: String. Filename and line number where the item is defined.
2187 :source: String. Filename and line number where the item is defined.
2188 :value: String. Config value.
2188 :value: String. Config value.
2189
2189
2190 The --shared flag can be used to edit the config file of shared source
2190 The --shared flag can be used to edit the config file of shared source
2191 repository. It only works when you have shared using the experimental
2191 repository. It only works when you have shared using the experimental
2192 share safe feature.
2192 share safe feature.
2193
2193
2194 Returns 0 on success, 1 if NAME does not exist.
2194 Returns 0 on success, 1 if NAME does not exist.
2195
2195
2196 """
2196 """
2197
2197
2198 opts = pycompat.byteskwargs(opts)
2198 opts = pycompat.byteskwargs(opts)
2199 editopts = (b'edit', b'local', b'global', b'shared')
2199 editopts = (b'edit', b'local', b'global', b'shared')
2200 if any(opts.get(o) for o in editopts):
2200 if any(opts.get(o) for o in editopts):
2201 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2201 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2202 if opts.get(b'local'):
2202 if opts.get(b'local'):
2203 if not repo:
2203 if not repo:
2204 raise error.Abort(_(b"can't use --local outside a repository"))
2204 raise error.Abort(_(b"can't use --local outside a repository"))
2205 paths = [repo.vfs.join(b'hgrc')]
2205 paths = [repo.vfs.join(b'hgrc')]
2206 elif opts.get(b'global'):
2206 elif opts.get(b'global'):
2207 paths = rcutil.systemrcpath()
2207 paths = rcutil.systemrcpath()
2208 elif opts.get(b'shared'):
2208 elif opts.get(b'shared'):
2209 if not repo.shared():
2209 if not repo.shared():
2210 raise error.Abort(
2210 raise error.Abort(
2211 _(b"repository is not shared; can't use --shared")
2211 _(b"repository is not shared; can't use --shared")
2212 )
2212 )
2213 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2213 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2214 raise error.Abort(
2214 raise error.Abort(
2215 _(
2215 _(
2216 b"share safe feature not unabled; "
2216 b"share safe feature not unabled; "
2217 b"unable to edit shared source repository config"
2217 b"unable to edit shared source repository config"
2218 )
2218 )
2219 )
2219 )
2220 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2220 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2221 else:
2221 else:
2222 paths = rcutil.userrcpath()
2222 paths = rcutil.userrcpath()
2223
2223
2224 for f in paths:
2224 for f in paths:
2225 if os.path.exists(f):
2225 if os.path.exists(f):
2226 break
2226 break
2227 else:
2227 else:
2228 if opts.get(b'global'):
2228 if opts.get(b'global'):
2229 samplehgrc = uimod.samplehgrcs[b'global']
2229 samplehgrc = uimod.samplehgrcs[b'global']
2230 elif opts.get(b'local'):
2230 elif opts.get(b'local'):
2231 samplehgrc = uimod.samplehgrcs[b'local']
2231 samplehgrc = uimod.samplehgrcs[b'local']
2232 else:
2232 else:
2233 samplehgrc = uimod.samplehgrcs[b'user']
2233 samplehgrc = uimod.samplehgrcs[b'user']
2234
2234
2235 f = paths[0]
2235 f = paths[0]
2236 fp = open(f, b"wb")
2236 fp = open(f, b"wb")
2237 fp.write(util.tonativeeol(samplehgrc))
2237 fp.write(util.tonativeeol(samplehgrc))
2238 fp.close()
2238 fp.close()
2239
2239
2240 editor = ui.geteditor()
2240 editor = ui.geteditor()
2241 ui.system(
2241 ui.system(
2242 b"%s \"%s\"" % (editor, f),
2242 b"%s \"%s\"" % (editor, f),
2243 onerr=error.Abort,
2243 onerr=error.Abort,
2244 errprefix=_(b"edit failed"),
2244 errprefix=_(b"edit failed"),
2245 blockedtag=b'config_edit',
2245 blockedtag=b'config_edit',
2246 )
2246 )
2247 return
2247 return
2248 ui.pager(b'config')
2248 ui.pager(b'config')
2249 fm = ui.formatter(b'config', opts)
2249 fm = ui.formatter(b'config', opts)
2250 for t, f in rcutil.rccomponents():
2250 for t, f in rcutil.rccomponents():
2251 if t == b'path':
2251 if t == b'path':
2252 ui.debug(b'read config from: %s\n' % f)
2252 ui.debug(b'read config from: %s\n' % f)
2253 elif t == b'resource':
2253 elif t == b'resource':
2254 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2254 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2255 elif t == b'items':
2255 elif t == b'items':
2256 # Don't print anything for 'items'.
2256 # Don't print anything for 'items'.
2257 pass
2257 pass
2258 else:
2258 else:
2259 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2259 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2260 untrusted = bool(opts.get(b'untrusted'))
2260 untrusted = bool(opts.get(b'untrusted'))
2261
2261
2262 selsections = selentries = []
2262 selsections = selentries = []
2263 if values:
2263 if values:
2264 selsections = [v for v in values if b'.' not in v]
2264 selsections = [v for v in values if b'.' not in v]
2265 selentries = [v for v in values if b'.' in v]
2265 selentries = [v for v in values if b'.' in v]
2266 uniquesel = len(selentries) == 1 and not selsections
2266 uniquesel = len(selentries) == 1 and not selsections
2267 selsections = set(selsections)
2267 selsections = set(selsections)
2268 selentries = set(selentries)
2268 selentries = set(selentries)
2269
2269
2270 matched = False
2270 matched = False
2271 for section, name, value in ui.walkconfig(untrusted=untrusted):
2271 for section, name, value in ui.walkconfig(untrusted=untrusted):
2272 source = ui.configsource(section, name, untrusted)
2272 source = ui.configsource(section, name, untrusted)
2273 value = pycompat.bytestr(value)
2273 value = pycompat.bytestr(value)
2274 defaultvalue = ui.configdefault(section, name)
2274 defaultvalue = ui.configdefault(section, name)
2275 if fm.isplain():
2275 if fm.isplain():
2276 source = source or b'none'
2276 source = source or b'none'
2277 value = value.replace(b'\n', b'\\n')
2277 value = value.replace(b'\n', b'\\n')
2278 entryname = section + b'.' + name
2278 entryname = section + b'.' + name
2279 if values and not (section in selsections or entryname in selentries):
2279 if values and not (section in selsections or entryname in selentries):
2280 continue
2280 continue
2281 fm.startitem()
2281 fm.startitem()
2282 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2282 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2283 if uniquesel:
2283 if uniquesel:
2284 fm.data(name=entryname)
2284 fm.data(name=entryname)
2285 fm.write(b'value', b'%s\n', value)
2285 fm.write(b'value', b'%s\n', value)
2286 else:
2286 else:
2287 fm.write(b'name value', b'%s=%s\n', entryname, value)
2287 fm.write(b'name value', b'%s=%s\n', entryname, value)
2288 if formatter.isprintable(defaultvalue):
2288 if formatter.isprintable(defaultvalue):
2289 fm.data(defaultvalue=defaultvalue)
2289 fm.data(defaultvalue=defaultvalue)
2290 elif isinstance(defaultvalue, list) and all(
2290 elif isinstance(defaultvalue, list) and all(
2291 formatter.isprintable(e) for e in defaultvalue
2291 formatter.isprintable(e) for e in defaultvalue
2292 ):
2292 ):
2293 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2293 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2294 # TODO: no idea how to process unsupported defaultvalue types
2294 # TODO: no idea how to process unsupported defaultvalue types
2295 matched = True
2295 matched = True
2296 fm.end()
2296 fm.end()
2297 if matched:
2297 if matched:
2298 return 0
2298 return 0
2299 return 1
2299 return 1
2300
2300
2301
2301
2302 @command(
2302 @command(
2303 b'continue',
2303 b'continue',
2304 dryrunopts,
2304 dryrunopts,
2305 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2305 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2306 helpbasic=True,
2306 helpbasic=True,
2307 )
2307 )
2308 def continuecmd(ui, repo, **opts):
2308 def continuecmd(ui, repo, **opts):
2309 """resumes an interrupted operation (EXPERIMENTAL)
2309 """resumes an interrupted operation (EXPERIMENTAL)
2310
2310
2311 Finishes a multistep operation like graft, histedit, rebase, merge,
2311 Finishes a multistep operation like graft, histedit, rebase, merge,
2312 and unshelve if they are in an interrupted state.
2312 and unshelve if they are in an interrupted state.
2313
2313
2314 use --dry-run/-n to dry run the command.
2314 use --dry-run/-n to dry run the command.
2315 """
2315 """
2316 dryrun = opts.get('dry_run')
2316 dryrun = opts.get('dry_run')
2317 contstate = cmdutil.getunfinishedstate(repo)
2317 contstate = cmdutil.getunfinishedstate(repo)
2318 if not contstate:
2318 if not contstate:
2319 raise error.Abort(_(b'no operation in progress'))
2319 raise error.Abort(_(b'no operation in progress'))
2320 if not contstate.continuefunc:
2320 if not contstate.continuefunc:
2321 raise error.Abort(
2321 raise error.Abort(
2322 (
2322 (
2323 _(b"%s in progress but does not support 'hg continue'")
2323 _(b"%s in progress but does not support 'hg continue'")
2324 % (contstate._opname)
2324 % (contstate._opname)
2325 ),
2325 ),
2326 hint=contstate.continuemsg(),
2326 hint=contstate.continuemsg(),
2327 )
2327 )
2328 if dryrun:
2328 if dryrun:
2329 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2329 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2330 return
2330 return
2331 return contstate.continuefunc(ui, repo)
2331 return contstate.continuefunc(ui, repo)
2332
2332
2333
2333
2334 @command(
2334 @command(
2335 b'copy|cp',
2335 b'copy|cp',
2336 [
2336 [
2337 (b'', b'forget', None, _(b'unmark a file as copied')),
2337 (b'', b'forget', None, _(b'unmark a file as copied')),
2338 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2338 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2339 (
2339 (
2340 b'',
2340 b'',
2341 b'at-rev',
2341 b'at-rev',
2342 b'',
2342 b'',
2343 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2343 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2344 _(b'REV'),
2344 _(b'REV'),
2345 ),
2345 ),
2346 (
2346 (
2347 b'f',
2347 b'f',
2348 b'force',
2348 b'force',
2349 None,
2349 None,
2350 _(b'forcibly copy over an existing managed file'),
2350 _(b'forcibly copy over an existing managed file'),
2351 ),
2351 ),
2352 ]
2352 ]
2353 + walkopts
2353 + walkopts
2354 + dryrunopts,
2354 + dryrunopts,
2355 _(b'[OPTION]... SOURCE... DEST'),
2355 _(b'[OPTION]... SOURCE... DEST'),
2356 helpcategory=command.CATEGORY_FILE_CONTENTS,
2356 helpcategory=command.CATEGORY_FILE_CONTENTS,
2357 )
2357 )
2358 def copy(ui, repo, *pats, **opts):
2358 def copy(ui, repo, *pats, **opts):
2359 """mark files as copied for the next commit
2359 """mark files as copied for the next commit
2360
2360
2361 Mark dest as having copies of source files. If dest is a
2361 Mark dest as having copies of source files. If dest is a
2362 directory, copies are put in that directory. If dest is a file,
2362 directory, copies are put in that directory. If dest is a file,
2363 the source must be a single file.
2363 the source must be a single file.
2364
2364
2365 By default, this command copies the contents of files as they
2365 By default, this command copies the contents of files as they
2366 exist in the working directory. If invoked with -A/--after, the
2366 exist in the working directory. If invoked with -A/--after, the
2367 operation is recorded, but no copying is performed.
2367 operation is recorded, but no copying is performed.
2368
2368
2369 To undo marking a file as copied, use --forget. With that option,
2369 To undo marking a file as copied, use --forget. With that option,
2370 all given (positional) arguments are unmarked as copies. The destination
2370 all given (positional) arguments are unmarked as copies. The destination
2371 file(s) will be left in place (still tracked).
2371 file(s) will be left in place (still tracked).
2372
2372
2373 This command takes effect with the next commit by default.
2373 This command takes effect with the next commit by default.
2374
2374
2375 Returns 0 on success, 1 if errors are encountered.
2375 Returns 0 on success, 1 if errors are encountered.
2376 """
2376 """
2377 opts = pycompat.byteskwargs(opts)
2377 opts = pycompat.byteskwargs(opts)
2378 with repo.wlock():
2378 with repo.wlock():
2379 return cmdutil.copy(ui, repo, pats, opts)
2379 return cmdutil.copy(ui, repo, pats, opts)
2380
2380
2381
2381
2382 @command(
2382 @command(
2383 b'debugcommands',
2383 b'debugcommands',
2384 [],
2384 [],
2385 _(b'[COMMAND]'),
2385 _(b'[COMMAND]'),
2386 helpcategory=command.CATEGORY_HELP,
2386 helpcategory=command.CATEGORY_HELP,
2387 norepo=True,
2387 norepo=True,
2388 )
2388 )
2389 def debugcommands(ui, cmd=b'', *args):
2389 def debugcommands(ui, cmd=b'', *args):
2390 """list all available commands and options"""
2390 """list all available commands and options"""
2391 for cmd, vals in sorted(pycompat.iteritems(table)):
2391 for cmd, vals in sorted(pycompat.iteritems(table)):
2392 cmd = cmd.split(b'|')[0]
2392 cmd = cmd.split(b'|')[0]
2393 opts = b', '.join([i[1] for i in vals[1]])
2393 opts = b', '.join([i[1] for i in vals[1]])
2394 ui.write(b'%s: %s\n' % (cmd, opts))
2394 ui.write(b'%s: %s\n' % (cmd, opts))
2395
2395
2396
2396
2397 @command(
2397 @command(
2398 b'debugcomplete',
2398 b'debugcomplete',
2399 [(b'o', b'options', None, _(b'show the command options'))],
2399 [(b'o', b'options', None, _(b'show the command options'))],
2400 _(b'[-o] CMD'),
2400 _(b'[-o] CMD'),
2401 helpcategory=command.CATEGORY_HELP,
2401 helpcategory=command.CATEGORY_HELP,
2402 norepo=True,
2402 norepo=True,
2403 )
2403 )
2404 def debugcomplete(ui, cmd=b'', **opts):
2404 def debugcomplete(ui, cmd=b'', **opts):
2405 """returns the completion list associated with the given command"""
2405 """returns the completion list associated with the given command"""
2406
2406
2407 if opts.get('options'):
2407 if opts.get('options'):
2408 options = []
2408 options = []
2409 otables = [globalopts]
2409 otables = [globalopts]
2410 if cmd:
2410 if cmd:
2411 aliases, entry = cmdutil.findcmd(cmd, table, False)
2411 aliases, entry = cmdutil.findcmd(cmd, table, False)
2412 otables.append(entry[1])
2412 otables.append(entry[1])
2413 for t in otables:
2413 for t in otables:
2414 for o in t:
2414 for o in t:
2415 if b"(DEPRECATED)" in o[3]:
2415 if b"(DEPRECATED)" in o[3]:
2416 continue
2416 continue
2417 if o[0]:
2417 if o[0]:
2418 options.append(b'-%s' % o[0])
2418 options.append(b'-%s' % o[0])
2419 options.append(b'--%s' % o[1])
2419 options.append(b'--%s' % o[1])
2420 ui.write(b"%s\n" % b"\n".join(options))
2420 ui.write(b"%s\n" % b"\n".join(options))
2421 return
2421 return
2422
2422
2423 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2423 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2424 if ui.verbose:
2424 if ui.verbose:
2425 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2425 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2426 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2426 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2427
2427
2428
2428
2429 @command(
2429 @command(
2430 b'diff',
2430 b'diff',
2431 [
2431 [
2432 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2432 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2433 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2433 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2434 ]
2434 ]
2435 + diffopts
2435 + diffopts
2436 + diffopts2
2436 + diffopts2
2437 + walkopts
2437 + walkopts
2438 + subrepoopts,
2438 + subrepoopts,
2439 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2439 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2440 helpcategory=command.CATEGORY_FILE_CONTENTS,
2440 helpcategory=command.CATEGORY_FILE_CONTENTS,
2441 helpbasic=True,
2441 helpbasic=True,
2442 inferrepo=True,
2442 inferrepo=True,
2443 intents={INTENT_READONLY},
2443 intents={INTENT_READONLY},
2444 )
2444 )
2445 def diff(ui, repo, *pats, **opts):
2445 def diff(ui, repo, *pats, **opts):
2446 """diff repository (or selected files)
2446 """diff repository (or selected files)
2447
2447
2448 Show differences between revisions for the specified files.
2448 Show differences between revisions for the specified files.
2449
2449
2450 Differences between files are shown using the unified diff format.
2450 Differences between files are shown using the unified diff format.
2451
2451
2452 .. note::
2452 .. note::
2453
2453
2454 :hg:`diff` may generate unexpected results for merges, as it will
2454 :hg:`diff` may generate unexpected results for merges, as it will
2455 default to comparing against the working directory's first
2455 default to comparing against the working directory's first
2456 parent changeset if no revisions are specified.
2456 parent changeset if no revisions are specified.
2457
2457
2458 When two revision arguments are given, then changes are shown
2458 When two revision arguments are given, then changes are shown
2459 between those revisions. If only one revision is specified then
2459 between those revisions. If only one revision is specified then
2460 that revision is compared to the working directory, and, when no
2460 that revision is compared to the working directory, and, when no
2461 revisions are specified, the working directory files are compared
2461 revisions are specified, the working directory files are compared
2462 to its first parent.
2462 to its first parent.
2463
2463
2464 Alternatively you can specify -c/--change with a revision to see
2464 Alternatively you can specify -c/--change with a revision to see
2465 the changes in that changeset relative to its first parent.
2465 the changes in that changeset relative to its first parent.
2466
2466
2467 Without the -a/--text option, diff will avoid generating diffs of
2467 Without the -a/--text option, diff will avoid generating diffs of
2468 files it detects as binary. With -a, diff will generate a diff
2468 files it detects as binary. With -a, diff will generate a diff
2469 anyway, probably with undesirable results.
2469 anyway, probably with undesirable results.
2470
2470
2471 Use the -g/--git option to generate diffs in the git extended diff
2471 Use the -g/--git option to generate diffs in the git extended diff
2472 format. For more information, read :hg:`help diffs`.
2472 format. For more information, read :hg:`help diffs`.
2473
2473
2474 .. container:: verbose
2474 .. container:: verbose
2475
2475
2476 Examples:
2476 Examples:
2477
2477
2478 - compare a file in the current working directory to its parent::
2478 - compare a file in the current working directory to its parent::
2479
2479
2480 hg diff foo.c
2480 hg diff foo.c
2481
2481
2482 - compare two historical versions of a directory, with rename info::
2482 - compare two historical versions of a directory, with rename info::
2483
2483
2484 hg diff --git -r 1.0:1.2 lib/
2484 hg diff --git -r 1.0:1.2 lib/
2485
2485
2486 - get change stats relative to the last change on some date::
2486 - get change stats relative to the last change on some date::
2487
2487
2488 hg diff --stat -r "date('may 2')"
2488 hg diff --stat -r "date('may 2')"
2489
2489
2490 - diff all newly-added files that contain a keyword::
2490 - diff all newly-added files that contain a keyword::
2491
2491
2492 hg diff "set:added() and grep(GNU)"
2492 hg diff "set:added() and grep(GNU)"
2493
2493
2494 - compare a revision and its parents::
2494 - compare a revision and its parents::
2495
2495
2496 hg diff -c 9353 # compare against first parent
2496 hg diff -c 9353 # compare against first parent
2497 hg diff -r 9353^:9353 # same using revset syntax
2497 hg diff -r 9353^:9353 # same using revset syntax
2498 hg diff -r 9353^2:9353 # compare against the second parent
2498 hg diff -r 9353^2:9353 # compare against the second parent
2499
2499
2500 Returns 0 on success.
2500 Returns 0 on success.
2501 """
2501 """
2502
2502
2503 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2503 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2504 opts = pycompat.byteskwargs(opts)
2504 opts = pycompat.byteskwargs(opts)
2505 revs = opts.get(b'rev')
2505 revs = opts.get(b'rev')
2506 change = opts.get(b'change')
2506 change = opts.get(b'change')
2507 stat = opts.get(b'stat')
2507 stat = opts.get(b'stat')
2508 reverse = opts.get(b'reverse')
2508 reverse = opts.get(b'reverse')
2509
2509
2510 if change:
2510 if change:
2511 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2511 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2512 ctx2 = scmutil.revsingle(repo, change, None)
2512 ctx2 = scmutil.revsingle(repo, change, None)
2513 ctx1 = ctx2.p1()
2513 ctx1 = ctx2.p1()
2514 else:
2514 else:
2515 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2515 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2516 ctx1, ctx2 = scmutil.revpair(repo, revs)
2516 ctx1, ctx2 = scmutil.revpair(repo, revs)
2517
2517
2518 if reverse:
2518 if reverse:
2519 ctxleft = ctx2
2519 ctxleft = ctx2
2520 ctxright = ctx1
2520 ctxright = ctx1
2521 else:
2521 else:
2522 ctxleft = ctx1
2522 ctxleft = ctx1
2523 ctxright = ctx2
2523 ctxright = ctx2
2524
2524
2525 diffopts = patch.diffallopts(ui, opts)
2525 diffopts = patch.diffallopts(ui, opts)
2526 m = scmutil.match(ctx2, pats, opts)
2526 m = scmutil.match(ctx2, pats, opts)
2527 m = repo.narrowmatch(m)
2527 m = repo.narrowmatch(m)
2528 ui.pager(b'diff')
2528 ui.pager(b'diff')
2529 logcmdutil.diffordiffstat(
2529 logcmdutil.diffordiffstat(
2530 ui,
2530 ui,
2531 repo,
2531 repo,
2532 diffopts,
2532 diffopts,
2533 ctxleft,
2533 ctxleft,
2534 ctxright,
2534 ctxright,
2535 m,
2535 m,
2536 stat=stat,
2536 stat=stat,
2537 listsubrepos=opts.get(b'subrepos'),
2537 listsubrepos=opts.get(b'subrepos'),
2538 root=opts.get(b'root'),
2538 root=opts.get(b'root'),
2539 )
2539 )
2540
2540
2541
2541
2542 @command(
2542 @command(
2543 b'export',
2543 b'export',
2544 [
2544 [
2545 (
2545 (
2546 b'B',
2546 b'B',
2547 b'bookmark',
2547 b'bookmark',
2548 b'',
2548 b'',
2549 _(b'export changes only reachable by given bookmark'),
2549 _(b'export changes only reachable by given bookmark'),
2550 _(b'BOOKMARK'),
2550 _(b'BOOKMARK'),
2551 ),
2551 ),
2552 (
2552 (
2553 b'o',
2553 b'o',
2554 b'output',
2554 b'output',
2555 b'',
2555 b'',
2556 _(b'print output to file with formatted name'),
2556 _(b'print output to file with formatted name'),
2557 _(b'FORMAT'),
2557 _(b'FORMAT'),
2558 ),
2558 ),
2559 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2559 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2560 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2560 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2561 ]
2561 ]
2562 + diffopts
2562 + diffopts
2563 + formatteropts,
2563 + formatteropts,
2564 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2564 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2565 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2565 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2566 helpbasic=True,
2566 helpbasic=True,
2567 intents={INTENT_READONLY},
2567 intents={INTENT_READONLY},
2568 )
2568 )
2569 def export(ui, repo, *changesets, **opts):
2569 def export(ui, repo, *changesets, **opts):
2570 """dump the header and diffs for one or more changesets
2570 """dump the header and diffs for one or more changesets
2571
2571
2572 Print the changeset header and diffs for one or more revisions.
2572 Print the changeset header and diffs for one or more revisions.
2573 If no revision is given, the parent of the working directory is used.
2573 If no revision is given, the parent of the working directory is used.
2574
2574
2575 The information shown in the changeset header is: author, date,
2575 The information shown in the changeset header is: author, date,
2576 branch name (if non-default), changeset hash, parent(s) and commit
2576 branch name (if non-default), changeset hash, parent(s) and commit
2577 comment.
2577 comment.
2578
2578
2579 .. note::
2579 .. note::
2580
2580
2581 :hg:`export` may generate unexpected diff output for merge
2581 :hg:`export` may generate unexpected diff output for merge
2582 changesets, as it will compare the merge changeset against its
2582 changesets, as it will compare the merge changeset against its
2583 first parent only.
2583 first parent only.
2584
2584
2585 Output may be to a file, in which case the name of the file is
2585 Output may be to a file, in which case the name of the file is
2586 given using a template string. See :hg:`help templates`. In addition
2586 given using a template string. See :hg:`help templates`. In addition
2587 to the common template keywords, the following formatting rules are
2587 to the common template keywords, the following formatting rules are
2588 supported:
2588 supported:
2589
2589
2590 :``%%``: literal "%" character
2590 :``%%``: literal "%" character
2591 :``%H``: changeset hash (40 hexadecimal digits)
2591 :``%H``: changeset hash (40 hexadecimal digits)
2592 :``%N``: number of patches being generated
2592 :``%N``: number of patches being generated
2593 :``%R``: changeset revision number
2593 :``%R``: changeset revision number
2594 :``%b``: basename of the exporting repository
2594 :``%b``: basename of the exporting repository
2595 :``%h``: short-form changeset hash (12 hexadecimal digits)
2595 :``%h``: short-form changeset hash (12 hexadecimal digits)
2596 :``%m``: first line of the commit message (only alphanumeric characters)
2596 :``%m``: first line of the commit message (only alphanumeric characters)
2597 :``%n``: zero-padded sequence number, starting at 1
2597 :``%n``: zero-padded sequence number, starting at 1
2598 :``%r``: zero-padded changeset revision number
2598 :``%r``: zero-padded changeset revision number
2599 :``\\``: literal "\\" character
2599 :``\\``: literal "\\" character
2600
2600
2601 Without the -a/--text option, export will avoid generating diffs
2601 Without the -a/--text option, export will avoid generating diffs
2602 of files it detects as binary. With -a, export will generate a
2602 of files it detects as binary. With -a, export will generate a
2603 diff anyway, probably with undesirable results.
2603 diff anyway, probably with undesirable results.
2604
2604
2605 With -B/--bookmark changesets reachable by the given bookmark are
2605 With -B/--bookmark changesets reachable by the given bookmark are
2606 selected.
2606 selected.
2607
2607
2608 Use the -g/--git option to generate diffs in the git extended diff
2608 Use the -g/--git option to generate diffs in the git extended diff
2609 format. See :hg:`help diffs` for more information.
2609 format. See :hg:`help diffs` for more information.
2610
2610
2611 With the --switch-parent option, the diff will be against the
2611 With the --switch-parent option, the diff will be against the
2612 second parent. It can be useful to review a merge.
2612 second parent. It can be useful to review a merge.
2613
2613
2614 .. container:: verbose
2614 .. container:: verbose
2615
2615
2616 Template:
2616 Template:
2617
2617
2618 The following keywords are supported in addition to the common template
2618 The following keywords are supported in addition to the common template
2619 keywords and functions. See also :hg:`help templates`.
2619 keywords and functions. See also :hg:`help templates`.
2620
2620
2621 :diff: String. Diff content.
2621 :diff: String. Diff content.
2622 :parents: List of strings. Parent nodes of the changeset.
2622 :parents: List of strings. Parent nodes of the changeset.
2623
2623
2624 Examples:
2624 Examples:
2625
2625
2626 - use export and import to transplant a bugfix to the current
2626 - use export and import to transplant a bugfix to the current
2627 branch::
2627 branch::
2628
2628
2629 hg export -r 9353 | hg import -
2629 hg export -r 9353 | hg import -
2630
2630
2631 - export all the changesets between two revisions to a file with
2631 - export all the changesets between two revisions to a file with
2632 rename information::
2632 rename information::
2633
2633
2634 hg export --git -r 123:150 > changes.txt
2634 hg export --git -r 123:150 > changes.txt
2635
2635
2636 - split outgoing changes into a series of patches with
2636 - split outgoing changes into a series of patches with
2637 descriptive names::
2637 descriptive names::
2638
2638
2639 hg export -r "outgoing()" -o "%n-%m.patch"
2639 hg export -r "outgoing()" -o "%n-%m.patch"
2640
2640
2641 Returns 0 on success.
2641 Returns 0 on success.
2642 """
2642 """
2643 opts = pycompat.byteskwargs(opts)
2643 opts = pycompat.byteskwargs(opts)
2644 bookmark = opts.get(b'bookmark')
2644 bookmark = opts.get(b'bookmark')
2645 changesets += tuple(opts.get(b'rev', []))
2645 changesets += tuple(opts.get(b'rev', []))
2646
2646
2647 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2647 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2648
2648
2649 if bookmark:
2649 if bookmark:
2650 if bookmark not in repo._bookmarks:
2650 if bookmark not in repo._bookmarks:
2651 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2651 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2652
2652
2653 revs = scmutil.bookmarkrevs(repo, bookmark)
2653 revs = scmutil.bookmarkrevs(repo, bookmark)
2654 else:
2654 else:
2655 if not changesets:
2655 if not changesets:
2656 changesets = [b'.']
2656 changesets = [b'.']
2657
2657
2658 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2658 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2659 revs = scmutil.revrange(repo, changesets)
2659 revs = scmutil.revrange(repo, changesets)
2660
2660
2661 if not revs:
2661 if not revs:
2662 raise error.Abort(_(b"export requires at least one changeset"))
2662 raise error.Abort(_(b"export requires at least one changeset"))
2663 if len(revs) > 1:
2663 if len(revs) > 1:
2664 ui.note(_(b'exporting patches:\n'))
2664 ui.note(_(b'exporting patches:\n'))
2665 else:
2665 else:
2666 ui.note(_(b'exporting patch:\n'))
2666 ui.note(_(b'exporting patch:\n'))
2667
2667
2668 fntemplate = opts.get(b'output')
2668 fntemplate = opts.get(b'output')
2669 if cmdutil.isstdiofilename(fntemplate):
2669 if cmdutil.isstdiofilename(fntemplate):
2670 fntemplate = b''
2670 fntemplate = b''
2671
2671
2672 if fntemplate:
2672 if fntemplate:
2673 fm = formatter.nullformatter(ui, b'export', opts)
2673 fm = formatter.nullformatter(ui, b'export', opts)
2674 else:
2674 else:
2675 ui.pager(b'export')
2675 ui.pager(b'export')
2676 fm = ui.formatter(b'export', opts)
2676 fm = ui.formatter(b'export', opts)
2677 with fm:
2677 with fm:
2678 cmdutil.export(
2678 cmdutil.export(
2679 repo,
2679 repo,
2680 revs,
2680 revs,
2681 fm,
2681 fm,
2682 fntemplate=fntemplate,
2682 fntemplate=fntemplate,
2683 switch_parent=opts.get(b'switch_parent'),
2683 switch_parent=opts.get(b'switch_parent'),
2684 opts=patch.diffallopts(ui, opts),
2684 opts=patch.diffallopts(ui, opts),
2685 )
2685 )
2686
2686
2687
2687
2688 @command(
2688 @command(
2689 b'files',
2689 b'files',
2690 [
2690 [
2691 (
2691 (
2692 b'r',
2692 b'r',
2693 b'rev',
2693 b'rev',
2694 b'',
2694 b'',
2695 _(b'search the repository as it is in REV'),
2695 _(b'search the repository as it is in REV'),
2696 _(b'REV'),
2696 _(b'REV'),
2697 ),
2697 ),
2698 (
2698 (
2699 b'0',
2699 b'0',
2700 b'print0',
2700 b'print0',
2701 None,
2701 None,
2702 _(b'end filenames with NUL, for use with xargs'),
2702 _(b'end filenames with NUL, for use with xargs'),
2703 ),
2703 ),
2704 ]
2704 ]
2705 + walkopts
2705 + walkopts
2706 + formatteropts
2706 + formatteropts
2707 + subrepoopts,
2707 + subrepoopts,
2708 _(b'[OPTION]... [FILE]...'),
2708 _(b'[OPTION]... [FILE]...'),
2709 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2709 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2710 intents={INTENT_READONLY},
2710 intents={INTENT_READONLY},
2711 )
2711 )
2712 def files(ui, repo, *pats, **opts):
2712 def files(ui, repo, *pats, **opts):
2713 """list tracked files
2713 """list tracked files
2714
2714
2715 Print files under Mercurial control in the working directory or
2715 Print files under Mercurial control in the working directory or
2716 specified revision for given files (excluding removed files).
2716 specified revision for given files (excluding removed files).
2717 Files can be specified as filenames or filesets.
2717 Files can be specified as filenames or filesets.
2718
2718
2719 If no files are given to match, this command prints the names
2719 If no files are given to match, this command prints the names
2720 of all files under Mercurial control.
2720 of all files under Mercurial control.
2721
2721
2722 .. container:: verbose
2722 .. container:: verbose
2723
2723
2724 Template:
2724 Template:
2725
2725
2726 The following keywords are supported in addition to the common template
2726 The following keywords are supported in addition to the common template
2727 keywords and functions. See also :hg:`help templates`.
2727 keywords and functions. See also :hg:`help templates`.
2728
2728
2729 :flags: String. Character denoting file's symlink and executable bits.
2729 :flags: String. Character denoting file's symlink and executable bits.
2730 :path: String. Repository-absolute path of the file.
2730 :path: String. Repository-absolute path of the file.
2731 :size: Integer. Size of the file in bytes.
2731 :size: Integer. Size of the file in bytes.
2732
2732
2733 Examples:
2733 Examples:
2734
2734
2735 - list all files under the current directory::
2735 - list all files under the current directory::
2736
2736
2737 hg files .
2737 hg files .
2738
2738
2739 - shows sizes and flags for current revision::
2739 - shows sizes and flags for current revision::
2740
2740
2741 hg files -vr .
2741 hg files -vr .
2742
2742
2743 - list all files named README::
2743 - list all files named README::
2744
2744
2745 hg files -I "**/README"
2745 hg files -I "**/README"
2746
2746
2747 - list all binary files::
2747 - list all binary files::
2748
2748
2749 hg files "set:binary()"
2749 hg files "set:binary()"
2750
2750
2751 - find files containing a regular expression::
2751 - find files containing a regular expression::
2752
2752
2753 hg files "set:grep('bob')"
2753 hg files "set:grep('bob')"
2754
2754
2755 - search tracked file contents with xargs and grep::
2755 - search tracked file contents with xargs and grep::
2756
2756
2757 hg files -0 | xargs -0 grep foo
2757 hg files -0 | xargs -0 grep foo
2758
2758
2759 See :hg:`help patterns` and :hg:`help filesets` for more information
2759 See :hg:`help patterns` and :hg:`help filesets` for more information
2760 on specifying file patterns.
2760 on specifying file patterns.
2761
2761
2762 Returns 0 if a match is found, 1 otherwise.
2762 Returns 0 if a match is found, 1 otherwise.
2763
2763
2764 """
2764 """
2765
2765
2766 opts = pycompat.byteskwargs(opts)
2766 opts = pycompat.byteskwargs(opts)
2767 rev = opts.get(b'rev')
2767 rev = opts.get(b'rev')
2768 if rev:
2768 if rev:
2769 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2769 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2770 ctx = scmutil.revsingle(repo, rev, None)
2770 ctx = scmutil.revsingle(repo, rev, None)
2771
2771
2772 end = b'\n'
2772 end = b'\n'
2773 if opts.get(b'print0'):
2773 if opts.get(b'print0'):
2774 end = b'\0'
2774 end = b'\0'
2775 fmt = b'%s' + end
2775 fmt = b'%s' + end
2776
2776
2777 m = scmutil.match(ctx, pats, opts)
2777 m = scmutil.match(ctx, pats, opts)
2778 ui.pager(b'files')
2778 ui.pager(b'files')
2779 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2779 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2780 with ui.formatter(b'files', opts) as fm:
2780 with ui.formatter(b'files', opts) as fm:
2781 return cmdutil.files(
2781 return cmdutil.files(
2782 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2782 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2783 )
2783 )
2784
2784
2785
2785
2786 @command(
2786 @command(
2787 b'forget',
2787 b'forget',
2788 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2788 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2789 + walkopts
2789 + walkopts
2790 + dryrunopts,
2790 + dryrunopts,
2791 _(b'[OPTION]... FILE...'),
2791 _(b'[OPTION]... FILE...'),
2792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2793 helpbasic=True,
2793 helpbasic=True,
2794 inferrepo=True,
2794 inferrepo=True,
2795 )
2795 )
2796 def forget(ui, repo, *pats, **opts):
2796 def forget(ui, repo, *pats, **opts):
2797 """forget the specified files on the next commit
2797 """forget the specified files on the next commit
2798
2798
2799 Mark the specified files so they will no longer be tracked
2799 Mark the specified files so they will no longer be tracked
2800 after the next commit.
2800 after the next commit.
2801
2801
2802 This only removes files from the current branch, not from the
2802 This only removes files from the current branch, not from the
2803 entire project history, and it does not delete them from the
2803 entire project history, and it does not delete them from the
2804 working directory.
2804 working directory.
2805
2805
2806 To delete the file from the working directory, see :hg:`remove`.
2806 To delete the file from the working directory, see :hg:`remove`.
2807
2807
2808 To undo a forget before the next commit, see :hg:`add`.
2808 To undo a forget before the next commit, see :hg:`add`.
2809
2809
2810 .. container:: verbose
2810 .. container:: verbose
2811
2811
2812 Examples:
2812 Examples:
2813
2813
2814 - forget newly-added binary files::
2814 - forget newly-added binary files::
2815
2815
2816 hg forget "set:added() and binary()"
2816 hg forget "set:added() and binary()"
2817
2817
2818 - forget files that would be excluded by .hgignore::
2818 - forget files that would be excluded by .hgignore::
2819
2819
2820 hg forget "set:hgignore()"
2820 hg forget "set:hgignore()"
2821
2821
2822 Returns 0 on success.
2822 Returns 0 on success.
2823 """
2823 """
2824
2824
2825 opts = pycompat.byteskwargs(opts)
2825 opts = pycompat.byteskwargs(opts)
2826 if not pats:
2826 if not pats:
2827 raise error.Abort(_(b'no files specified'))
2827 raise error.Abort(_(b'no files specified'))
2828
2828
2829 m = scmutil.match(repo[None], pats, opts)
2829 m = scmutil.match(repo[None], pats, opts)
2830 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2830 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2831 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2831 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2832 rejected = cmdutil.forget(
2832 rejected = cmdutil.forget(
2833 ui,
2833 ui,
2834 repo,
2834 repo,
2835 m,
2835 m,
2836 prefix=b"",
2836 prefix=b"",
2837 uipathfn=uipathfn,
2837 uipathfn=uipathfn,
2838 explicitonly=False,
2838 explicitonly=False,
2839 dryrun=dryrun,
2839 dryrun=dryrun,
2840 interactive=interactive,
2840 interactive=interactive,
2841 )[0]
2841 )[0]
2842 return rejected and 1 or 0
2842 return rejected and 1 or 0
2843
2843
2844
2844
2845 @command(
2845 @command(
2846 b'graft',
2846 b'graft',
2847 [
2847 [
2848 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2848 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2849 (
2849 (
2850 b'',
2850 b'',
2851 b'base',
2851 b'base',
2852 b'',
2852 b'',
2853 _(b'base revision when doing the graft merge (ADVANCED)'),
2853 _(b'base revision when doing the graft merge (ADVANCED)'),
2854 _(b'REV'),
2854 _(b'REV'),
2855 ),
2855 ),
2856 (b'c', b'continue', False, _(b'resume interrupted graft')),
2856 (b'c', b'continue', False, _(b'resume interrupted graft')),
2857 (b'', b'stop', False, _(b'stop interrupted graft')),
2857 (b'', b'stop', False, _(b'stop interrupted graft')),
2858 (b'', b'abort', False, _(b'abort interrupted graft')),
2858 (b'', b'abort', False, _(b'abort interrupted graft')),
2859 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2859 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2860 (b'', b'log', None, _(b'append graft info to log message')),
2860 (b'', b'log', None, _(b'append graft info to log message')),
2861 (
2861 (
2862 b'',
2862 b'',
2863 b'no-commit',
2863 b'no-commit',
2864 None,
2864 None,
2865 _(b"don't commit, just apply the changes in working directory"),
2865 _(b"don't commit, just apply the changes in working directory"),
2866 ),
2866 ),
2867 (b'f', b'force', False, _(b'force graft')),
2867 (b'f', b'force', False, _(b'force graft')),
2868 (
2868 (
2869 b'D',
2869 b'D',
2870 b'currentdate',
2870 b'currentdate',
2871 False,
2871 False,
2872 _(b'record the current date as commit date'),
2872 _(b'record the current date as commit date'),
2873 ),
2873 ),
2874 (
2874 (
2875 b'U',
2875 b'U',
2876 b'currentuser',
2876 b'currentuser',
2877 False,
2877 False,
2878 _(b'record the current user as committer'),
2878 _(b'record the current user as committer'),
2879 ),
2879 ),
2880 ]
2880 ]
2881 + commitopts2
2881 + commitopts2
2882 + mergetoolopts
2882 + mergetoolopts
2883 + dryrunopts,
2883 + dryrunopts,
2884 _(b'[OPTION]... [-r REV]... REV...'),
2884 _(b'[OPTION]... [-r REV]... REV...'),
2885 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2885 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2886 )
2886 )
2887 def graft(ui, repo, *revs, **opts):
2887 def graft(ui, repo, *revs, **opts):
2888 '''copy changes from other branches onto the current branch
2888 '''copy changes from other branches onto the current branch
2889
2889
2890 This command uses Mercurial's merge logic to copy individual
2890 This command uses Mercurial's merge logic to copy individual
2891 changes from other branches without merging branches in the
2891 changes from other branches without merging branches in the
2892 history graph. This is sometimes known as 'backporting' or
2892 history graph. This is sometimes known as 'backporting' or
2893 'cherry-picking'. By default, graft will copy user, date, and
2893 'cherry-picking'. By default, graft will copy user, date, and
2894 description from the source changesets.
2894 description from the source changesets.
2895
2895
2896 Changesets that are ancestors of the current revision, that have
2896 Changesets that are ancestors of the current revision, that have
2897 already been grafted, or that are merges will be skipped.
2897 already been grafted, or that are merges will be skipped.
2898
2898
2899 If --log is specified, log messages will have a comment appended
2899 If --log is specified, log messages will have a comment appended
2900 of the form::
2900 of the form::
2901
2901
2902 (grafted from CHANGESETHASH)
2902 (grafted from CHANGESETHASH)
2903
2903
2904 If --force is specified, revisions will be grafted even if they
2904 If --force is specified, revisions will be grafted even if they
2905 are already ancestors of, or have been grafted to, the destination.
2905 are already ancestors of, or have been grafted to, the destination.
2906 This is useful when the revisions have since been backed out.
2906 This is useful when the revisions have since been backed out.
2907
2907
2908 If a graft merge results in conflicts, the graft process is
2908 If a graft merge results in conflicts, the graft process is
2909 interrupted so that the current merge can be manually resolved.
2909 interrupted so that the current merge can be manually resolved.
2910 Once all conflicts are addressed, the graft process can be
2910 Once all conflicts are addressed, the graft process can be
2911 continued with the -c/--continue option.
2911 continued with the -c/--continue option.
2912
2912
2913 The -c/--continue option reapplies all the earlier options.
2913 The -c/--continue option reapplies all the earlier options.
2914
2914
2915 .. container:: verbose
2915 .. container:: verbose
2916
2916
2917 The --base option exposes more of how graft internally uses merge with a
2917 The --base option exposes more of how graft internally uses merge with a
2918 custom base revision. --base can be used to specify another ancestor than
2918 custom base revision. --base can be used to specify another ancestor than
2919 the first and only parent.
2919 the first and only parent.
2920
2920
2921 The command::
2921 The command::
2922
2922
2923 hg graft -r 345 --base 234
2923 hg graft -r 345 --base 234
2924
2924
2925 is thus pretty much the same as::
2925 is thus pretty much the same as::
2926
2926
2927 hg diff -r 234 -r 345 | hg import
2927 hg diff -r 234 -r 345 | hg import
2928
2928
2929 but using merge to resolve conflicts and track moved files.
2929 but using merge to resolve conflicts and track moved files.
2930
2930
2931 The result of a merge can thus be backported as a single commit by
2931 The result of a merge can thus be backported as a single commit by
2932 specifying one of the merge parents as base, and thus effectively
2932 specifying one of the merge parents as base, and thus effectively
2933 grafting the changes from the other side.
2933 grafting the changes from the other side.
2934
2934
2935 It is also possible to collapse multiple changesets and clean up history
2935 It is also possible to collapse multiple changesets and clean up history
2936 by specifying another ancestor as base, much like rebase --collapse
2936 by specifying another ancestor as base, much like rebase --collapse
2937 --keep.
2937 --keep.
2938
2938
2939 The commit message can be tweaked after the fact using commit --amend .
2939 The commit message can be tweaked after the fact using commit --amend .
2940
2940
2941 For using non-ancestors as the base to backout changes, see the backout
2941 For using non-ancestors as the base to backout changes, see the backout
2942 command and the hidden --parent option.
2942 command and the hidden --parent option.
2943
2943
2944 .. container:: verbose
2944 .. container:: verbose
2945
2945
2946 Examples:
2946 Examples:
2947
2947
2948 - copy a single change to the stable branch and edit its description::
2948 - copy a single change to the stable branch and edit its description::
2949
2949
2950 hg update stable
2950 hg update stable
2951 hg graft --edit 9393
2951 hg graft --edit 9393
2952
2952
2953 - graft a range of changesets with one exception, updating dates::
2953 - graft a range of changesets with one exception, updating dates::
2954
2954
2955 hg graft -D "2085::2093 and not 2091"
2955 hg graft -D "2085::2093 and not 2091"
2956
2956
2957 - continue a graft after resolving conflicts::
2957 - continue a graft after resolving conflicts::
2958
2958
2959 hg graft -c
2959 hg graft -c
2960
2960
2961 - show the source of a grafted changeset::
2961 - show the source of a grafted changeset::
2962
2962
2963 hg log --debug -r .
2963 hg log --debug -r .
2964
2964
2965 - show revisions sorted by date::
2965 - show revisions sorted by date::
2966
2966
2967 hg log -r "sort(all(), date)"
2967 hg log -r "sort(all(), date)"
2968
2968
2969 - backport the result of a merge as a single commit::
2969 - backport the result of a merge as a single commit::
2970
2970
2971 hg graft -r 123 --base 123^
2971 hg graft -r 123 --base 123^
2972
2972
2973 - land a feature branch as one changeset::
2973 - land a feature branch as one changeset::
2974
2974
2975 hg up -cr default
2975 hg up -cr default
2976 hg graft -r featureX --base "ancestor('featureX', 'default')"
2976 hg graft -r featureX --base "ancestor('featureX', 'default')"
2977
2977
2978 See :hg:`help revisions` for more about specifying revisions.
2978 See :hg:`help revisions` for more about specifying revisions.
2979
2979
2980 Returns 0 on successful completion, 1 if there are unresolved files.
2980 Returns 0 on successful completion, 1 if there are unresolved files.
2981 '''
2981 '''
2982 with repo.wlock():
2982 with repo.wlock():
2983 return _dograft(ui, repo, *revs, **opts)
2983 return _dograft(ui, repo, *revs, **opts)
2984
2984
2985
2985
2986 def _dograft(ui, repo, *revs, **opts):
2986 def _dograft(ui, repo, *revs, **opts):
2987 opts = pycompat.byteskwargs(opts)
2987 opts = pycompat.byteskwargs(opts)
2988 if revs and opts.get(b'rev'):
2988 if revs and opts.get(b'rev'):
2989 ui.warn(
2989 ui.warn(
2990 _(
2990 _(
2991 b'warning: inconsistent use of --rev might give unexpected '
2991 b'warning: inconsistent use of --rev might give unexpected '
2992 b'revision ordering!\n'
2992 b'revision ordering!\n'
2993 )
2993 )
2994 )
2994 )
2995
2995
2996 revs = list(revs)
2996 revs = list(revs)
2997 revs.extend(opts.get(b'rev'))
2997 revs.extend(opts.get(b'rev'))
2998 # a dict of data to be stored in state file
2998 # a dict of data to be stored in state file
2999 statedata = {}
2999 statedata = {}
3000 # list of new nodes created by ongoing graft
3000 # list of new nodes created by ongoing graft
3001 statedata[b'newnodes'] = []
3001 statedata[b'newnodes'] = []
3002
3002
3003 cmdutil.resolvecommitoptions(ui, opts)
3003 cmdutil.resolvecommitoptions(ui, opts)
3004
3004
3005 editor = cmdutil.getcommiteditor(
3005 editor = cmdutil.getcommiteditor(
3006 editform=b'graft', **pycompat.strkwargs(opts)
3006 editform=b'graft', **pycompat.strkwargs(opts)
3007 )
3007 )
3008
3008
3009 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3009 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3010
3010
3011 cont = False
3011 cont = False
3012 if opts.get(b'no_commit'):
3012 if opts.get(b'no_commit'):
3013 cmdutil.check_incompatible_arguments(
3013 cmdutil.check_incompatible_arguments(
3014 opts,
3014 opts,
3015 b'no_commit',
3015 b'no_commit',
3016 [b'edit', b'currentuser', b'currentdate', b'log'],
3016 [b'edit', b'currentuser', b'currentdate', b'log'],
3017 )
3017 )
3018
3018
3019 graftstate = statemod.cmdstate(repo, b'graftstate')
3019 graftstate = statemod.cmdstate(repo, b'graftstate')
3020
3020
3021 if opts.get(b'stop'):
3021 if opts.get(b'stop'):
3022 cmdutil.check_incompatible_arguments(
3022 cmdutil.check_incompatible_arguments(
3023 opts,
3023 opts,
3024 b'stop',
3024 b'stop',
3025 [
3025 [
3026 b'edit',
3026 b'edit',
3027 b'log',
3027 b'log',
3028 b'user',
3028 b'user',
3029 b'date',
3029 b'date',
3030 b'currentdate',
3030 b'currentdate',
3031 b'currentuser',
3031 b'currentuser',
3032 b'rev',
3032 b'rev',
3033 ],
3033 ],
3034 )
3034 )
3035 return _stopgraft(ui, repo, graftstate)
3035 return _stopgraft(ui, repo, graftstate)
3036 elif opts.get(b'abort'):
3036 elif opts.get(b'abort'):
3037 cmdutil.check_incompatible_arguments(
3037 cmdutil.check_incompatible_arguments(
3038 opts,
3038 opts,
3039 b'abort',
3039 b'abort',
3040 [
3040 [
3041 b'edit',
3041 b'edit',
3042 b'log',
3042 b'log',
3043 b'user',
3043 b'user',
3044 b'date',
3044 b'date',
3045 b'currentdate',
3045 b'currentdate',
3046 b'currentuser',
3046 b'currentuser',
3047 b'rev',
3047 b'rev',
3048 ],
3048 ],
3049 )
3049 )
3050 return cmdutil.abortgraft(ui, repo, graftstate)
3050 return cmdutil.abortgraft(ui, repo, graftstate)
3051 elif opts.get(b'continue'):
3051 elif opts.get(b'continue'):
3052 cont = True
3052 cont = True
3053 if revs:
3053 if revs:
3054 raise error.Abort(_(b"can't specify --continue and revisions"))
3054 raise error.Abort(_(b"can't specify --continue and revisions"))
3055 # read in unfinished revisions
3055 # read in unfinished revisions
3056 if graftstate.exists():
3056 if graftstate.exists():
3057 statedata = cmdutil.readgraftstate(repo, graftstate)
3057 statedata = cmdutil.readgraftstate(repo, graftstate)
3058 if statedata.get(b'date'):
3058 if statedata.get(b'date'):
3059 opts[b'date'] = statedata[b'date']
3059 opts[b'date'] = statedata[b'date']
3060 if statedata.get(b'user'):
3060 if statedata.get(b'user'):
3061 opts[b'user'] = statedata[b'user']
3061 opts[b'user'] = statedata[b'user']
3062 if statedata.get(b'log'):
3062 if statedata.get(b'log'):
3063 opts[b'log'] = True
3063 opts[b'log'] = True
3064 if statedata.get(b'no_commit'):
3064 if statedata.get(b'no_commit'):
3065 opts[b'no_commit'] = statedata.get(b'no_commit')
3065 opts[b'no_commit'] = statedata.get(b'no_commit')
3066 if statedata.get(b'base'):
3066 if statedata.get(b'base'):
3067 opts[b'base'] = statedata.get(b'base')
3067 opts[b'base'] = statedata.get(b'base')
3068 nodes = statedata[b'nodes']
3068 nodes = statedata[b'nodes']
3069 revs = [repo[node].rev() for node in nodes]
3069 revs = [repo[node].rev() for node in nodes]
3070 else:
3070 else:
3071 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3071 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3072 else:
3072 else:
3073 if not revs:
3073 if not revs:
3074 raise error.Abort(_(b'no revisions specified'))
3074 raise error.Abort(_(b'no revisions specified'))
3075 cmdutil.checkunfinished(repo)
3075 cmdutil.checkunfinished(repo)
3076 cmdutil.bailifchanged(repo)
3076 cmdutil.bailifchanged(repo)
3077 revs = scmutil.revrange(repo, revs)
3077 revs = scmutil.revrange(repo, revs)
3078
3078
3079 skipped = set()
3079 skipped = set()
3080 basectx = None
3080 basectx = None
3081 if opts.get(b'base'):
3081 if opts.get(b'base'):
3082 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3082 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3083 if basectx is None:
3083 if basectx is None:
3084 # check for merges
3084 # check for merges
3085 for rev in repo.revs(b'%ld and merge()', revs):
3085 for rev in repo.revs(b'%ld and merge()', revs):
3086 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3086 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3087 skipped.add(rev)
3087 skipped.add(rev)
3088 revs = [r for r in revs if r not in skipped]
3088 revs = [r for r in revs if r not in skipped]
3089 if not revs:
3089 if not revs:
3090 return -1
3090 return -1
3091 if basectx is not None and len(revs) != 1:
3091 if basectx is not None and len(revs) != 1:
3092 raise error.Abort(_(b'only one revision allowed with --base '))
3092 raise error.Abort(_(b'only one revision allowed with --base '))
3093
3093
3094 # Don't check in the --continue case, in effect retaining --force across
3094 # Don't check in the --continue case, in effect retaining --force across
3095 # --continues. That's because without --force, any revisions we decided to
3095 # --continues. That's because without --force, any revisions we decided to
3096 # skip would have been filtered out here, so they wouldn't have made their
3096 # skip would have been filtered out here, so they wouldn't have made their
3097 # way to the graftstate. With --force, any revisions we would have otherwise
3097 # way to the graftstate. With --force, any revisions we would have otherwise
3098 # skipped would not have been filtered out, and if they hadn't been applied
3098 # skipped would not have been filtered out, and if they hadn't been applied
3099 # already, they'd have been in the graftstate.
3099 # already, they'd have been in the graftstate.
3100 if not (cont or opts.get(b'force')) and basectx is None:
3100 if not (cont or opts.get(b'force')) and basectx is None:
3101 # check for ancestors of dest branch
3101 # check for ancestors of dest branch
3102 ancestors = repo.revs(b'%ld & (::.)', revs)
3102 ancestors = repo.revs(b'%ld & (::.)', revs)
3103 for rev in ancestors:
3103 for rev in ancestors:
3104 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3104 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3105
3105
3106 revs = [r for r in revs if r not in ancestors]
3106 revs = [r for r in revs if r not in ancestors]
3107
3107
3108 if not revs:
3108 if not revs:
3109 return -1
3109 return -1
3110
3110
3111 # analyze revs for earlier grafts
3111 # analyze revs for earlier grafts
3112 ids = {}
3112 ids = {}
3113 for ctx in repo.set(b"%ld", revs):
3113 for ctx in repo.set(b"%ld", revs):
3114 ids[ctx.hex()] = ctx.rev()
3114 ids[ctx.hex()] = ctx.rev()
3115 n = ctx.extra().get(b'source')
3115 n = ctx.extra().get(b'source')
3116 if n:
3116 if n:
3117 ids[n] = ctx.rev()
3117 ids[n] = ctx.rev()
3118
3118
3119 # check ancestors for earlier grafts
3119 # check ancestors for earlier grafts
3120 ui.debug(b'scanning for duplicate grafts\n')
3120 ui.debug(b'scanning for duplicate grafts\n')
3121
3121
3122 # The only changesets we can be sure doesn't contain grafts of any
3122 # The only changesets we can be sure doesn't contain grafts of any
3123 # revs, are the ones that are common ancestors of *all* revs:
3123 # revs, are the ones that are common ancestors of *all* revs:
3124 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3124 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3125 ctx = repo[rev]
3125 ctx = repo[rev]
3126 n = ctx.extra().get(b'source')
3126 n = ctx.extra().get(b'source')
3127 if n in ids:
3127 if n in ids:
3128 try:
3128 try:
3129 r = repo[n].rev()
3129 r = repo[n].rev()
3130 except error.RepoLookupError:
3130 except error.RepoLookupError:
3131 r = None
3131 r = None
3132 if r in revs:
3132 if r in revs:
3133 ui.warn(
3133 ui.warn(
3134 _(
3134 _(
3135 b'skipping revision %d:%s '
3135 b'skipping revision %d:%s '
3136 b'(already grafted to %d:%s)\n'
3136 b'(already grafted to %d:%s)\n'
3137 )
3137 )
3138 % (r, repo[r], rev, ctx)
3138 % (r, repo[r], rev, ctx)
3139 )
3139 )
3140 revs.remove(r)
3140 revs.remove(r)
3141 elif ids[n] in revs:
3141 elif ids[n] in revs:
3142 if r is None:
3142 if r is None:
3143 ui.warn(
3143 ui.warn(
3144 _(
3144 _(
3145 b'skipping already grafted revision %d:%s '
3145 b'skipping already grafted revision %d:%s '
3146 b'(%d:%s also has unknown origin %s)\n'
3146 b'(%d:%s also has unknown origin %s)\n'
3147 )
3147 )
3148 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3148 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3149 )
3149 )
3150 else:
3150 else:
3151 ui.warn(
3151 ui.warn(
3152 _(
3152 _(
3153 b'skipping already grafted revision %d:%s '
3153 b'skipping already grafted revision %d:%s '
3154 b'(%d:%s also has origin %d:%s)\n'
3154 b'(%d:%s also has origin %d:%s)\n'
3155 )
3155 )
3156 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3156 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3157 )
3157 )
3158 revs.remove(ids[n])
3158 revs.remove(ids[n])
3159 elif ctx.hex() in ids:
3159 elif ctx.hex() in ids:
3160 r = ids[ctx.hex()]
3160 r = ids[ctx.hex()]
3161 if r in revs:
3161 if r in revs:
3162 ui.warn(
3162 ui.warn(
3163 _(
3163 _(
3164 b'skipping already grafted revision %d:%s '
3164 b'skipping already grafted revision %d:%s '
3165 b'(was grafted from %d:%s)\n'
3165 b'(was grafted from %d:%s)\n'
3166 )
3166 )
3167 % (r, repo[r], rev, ctx)
3167 % (r, repo[r], rev, ctx)
3168 )
3168 )
3169 revs.remove(r)
3169 revs.remove(r)
3170 if not revs:
3170 if not revs:
3171 return -1
3171 return -1
3172
3172
3173 if opts.get(b'no_commit'):
3173 if opts.get(b'no_commit'):
3174 statedata[b'no_commit'] = True
3174 statedata[b'no_commit'] = True
3175 if opts.get(b'base'):
3175 if opts.get(b'base'):
3176 statedata[b'base'] = opts[b'base']
3176 statedata[b'base'] = opts[b'base']
3177 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3177 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3178 desc = b'%d:%s "%s"' % (
3178 desc = b'%d:%s "%s"' % (
3179 ctx.rev(),
3179 ctx.rev(),
3180 ctx,
3180 ctx,
3181 ctx.description().split(b'\n', 1)[0],
3181 ctx.description().split(b'\n', 1)[0],
3182 )
3182 )
3183 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3183 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3184 if names:
3184 if names:
3185 desc += b' (%s)' % b' '.join(names)
3185 desc += b' (%s)' % b' '.join(names)
3186 ui.status(_(b'grafting %s\n') % desc)
3186 ui.status(_(b'grafting %s\n') % desc)
3187 if opts.get(b'dry_run'):
3187 if opts.get(b'dry_run'):
3188 continue
3188 continue
3189
3189
3190 source = ctx.extra().get(b'source')
3190 source = ctx.extra().get(b'source')
3191 extra = {}
3191 extra = {}
3192 if source:
3192 if source:
3193 extra[b'source'] = source
3193 extra[b'source'] = source
3194 extra[b'intermediate-source'] = ctx.hex()
3194 extra[b'intermediate-source'] = ctx.hex()
3195 else:
3195 else:
3196 extra[b'source'] = ctx.hex()
3196 extra[b'source'] = ctx.hex()
3197 user = ctx.user()
3197 user = ctx.user()
3198 if opts.get(b'user'):
3198 if opts.get(b'user'):
3199 user = opts[b'user']
3199 user = opts[b'user']
3200 statedata[b'user'] = user
3200 statedata[b'user'] = user
3201 date = ctx.date()
3201 date = ctx.date()
3202 if opts.get(b'date'):
3202 if opts.get(b'date'):
3203 date = opts[b'date']
3203 date = opts[b'date']
3204 statedata[b'date'] = date
3204 statedata[b'date'] = date
3205 message = ctx.description()
3205 message = ctx.description()
3206 if opts.get(b'log'):
3206 if opts.get(b'log'):
3207 message += b'\n(grafted from %s)' % ctx.hex()
3207 message += b'\n(grafted from %s)' % ctx.hex()
3208 statedata[b'log'] = True
3208 statedata[b'log'] = True
3209
3209
3210 # we don't merge the first commit when continuing
3210 # we don't merge the first commit when continuing
3211 if not cont:
3211 if not cont:
3212 # perform the graft merge with p1(rev) as 'ancestor'
3212 # perform the graft merge with p1(rev) as 'ancestor'
3213 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3213 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3214 base = ctx.p1() if basectx is None else basectx
3214 base = ctx.p1() if basectx is None else basectx
3215 with ui.configoverride(overrides, b'graft'):
3215 with ui.configoverride(overrides, b'graft'):
3216 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3216 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3217 # report any conflicts
3217 # report any conflicts
3218 if stats.unresolvedcount > 0:
3218 if stats.unresolvedcount > 0:
3219 # write out state for --continue
3219 # write out state for --continue
3220 nodes = [repo[rev].hex() for rev in revs[pos:]]
3220 nodes = [repo[rev].hex() for rev in revs[pos:]]
3221 statedata[b'nodes'] = nodes
3221 statedata[b'nodes'] = nodes
3222 stateversion = 1
3222 stateversion = 1
3223 graftstate.save(stateversion, statedata)
3223 graftstate.save(stateversion, statedata)
3224 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3224 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3225 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3225 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3226 return 1
3226 return 1
3227 else:
3227 else:
3228 cont = False
3228 cont = False
3229
3229
3230 # commit if --no-commit is false
3230 # commit if --no-commit is false
3231 if not opts.get(b'no_commit'):
3231 if not opts.get(b'no_commit'):
3232 node = repo.commit(
3232 node = repo.commit(
3233 text=message, user=user, date=date, extra=extra, editor=editor
3233 text=message, user=user, date=date, extra=extra, editor=editor
3234 )
3234 )
3235 if node is None:
3235 if node is None:
3236 ui.warn(
3236 ui.warn(
3237 _(b'note: graft of %d:%s created no changes to commit\n')
3237 _(b'note: graft of %d:%s created no changes to commit\n')
3238 % (ctx.rev(), ctx)
3238 % (ctx.rev(), ctx)
3239 )
3239 )
3240 # checking that newnodes exist because old state files won't have it
3240 # checking that newnodes exist because old state files won't have it
3241 elif statedata.get(b'newnodes') is not None:
3241 elif statedata.get(b'newnodes') is not None:
3242 statedata[b'newnodes'].append(node)
3242 statedata[b'newnodes'].append(node)
3243
3243
3244 # remove state when we complete successfully
3244 # remove state when we complete successfully
3245 if not opts.get(b'dry_run'):
3245 if not opts.get(b'dry_run'):
3246 graftstate.delete()
3246 graftstate.delete()
3247
3247
3248 return 0
3248 return 0
3249
3249
3250
3250
3251 def _stopgraft(ui, repo, graftstate):
3251 def _stopgraft(ui, repo, graftstate):
3252 """stop the interrupted graft"""
3252 """stop the interrupted graft"""
3253 if not graftstate.exists():
3253 if not graftstate.exists():
3254 raise error.Abort(_(b"no interrupted graft found"))
3254 raise error.Abort(_(b"no interrupted graft found"))
3255 pctx = repo[b'.']
3255 pctx = repo[b'.']
3256 hg.updaterepo(repo, pctx.node(), overwrite=True)
3256 hg.updaterepo(repo, pctx.node(), overwrite=True)
3257 graftstate.delete()
3257 graftstate.delete()
3258 ui.status(_(b"stopped the interrupted graft\n"))
3258 ui.status(_(b"stopped the interrupted graft\n"))
3259 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3259 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3260 return 0
3260 return 0
3261
3261
3262
3262
3263 statemod.addunfinished(
3263 statemod.addunfinished(
3264 b'graft',
3264 b'graft',
3265 fname=b'graftstate',
3265 fname=b'graftstate',
3266 clearable=True,
3266 clearable=True,
3267 stopflag=True,
3267 stopflag=True,
3268 continueflag=True,
3268 continueflag=True,
3269 abortfunc=cmdutil.hgabortgraft,
3269 abortfunc=cmdutil.hgabortgraft,
3270 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3270 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3271 )
3271 )
3272
3272
3273
3273
3274 @command(
3274 @command(
3275 b'grep',
3275 b'grep',
3276 [
3276 [
3277 (b'0', b'print0', None, _(b'end fields with NUL')),
3277 (b'0', b'print0', None, _(b'end fields with NUL')),
3278 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3278 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3279 (
3279 (
3280 b'',
3280 b'',
3281 b'diff',
3281 b'diff',
3282 None,
3282 None,
3283 _(
3283 _(
3284 b'search revision differences for when the pattern was added '
3284 b'search revision differences for when the pattern was added '
3285 b'or removed'
3285 b'or removed'
3286 ),
3286 ),
3287 ),
3287 ),
3288 (b'a', b'text', None, _(b'treat all files as text')),
3288 (b'a', b'text', None, _(b'treat all files as text')),
3289 (
3289 (
3290 b'f',
3290 b'f',
3291 b'follow',
3291 b'follow',
3292 None,
3292 None,
3293 _(
3293 _(
3294 b'follow changeset history,'
3294 b'follow changeset history,'
3295 b' or file history across copies and renames'
3295 b' or file history across copies and renames'
3296 ),
3296 ),
3297 ),
3297 ),
3298 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3298 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3299 (
3299 (
3300 b'l',
3300 b'l',
3301 b'files-with-matches',
3301 b'files-with-matches',
3302 None,
3302 None,
3303 _(b'print only filenames and revisions that match'),
3303 _(b'print only filenames and revisions that match'),
3304 ),
3304 ),
3305 (b'n', b'line-number', None, _(b'print matching line numbers')),
3305 (b'n', b'line-number', None, _(b'print matching line numbers')),
3306 (
3306 (
3307 b'r',
3307 b'r',
3308 b'rev',
3308 b'rev',
3309 [],
3309 [],
3310 _(b'search files changed within revision range'),
3310 _(b'search files changed within revision range'),
3311 _(b'REV'),
3311 _(b'REV'),
3312 ),
3312 ),
3313 (
3313 (
3314 b'',
3314 b'',
3315 b'all-files',
3315 b'all-files',
3316 None,
3316 None,
3317 _(
3317 _(
3318 b'include all files in the changeset while grepping (DEPRECATED)'
3318 b'include all files in the changeset while grepping (DEPRECATED)'
3319 ),
3319 ),
3320 ),
3320 ),
3321 (b'u', b'user', None, _(b'list the author (long with -v)')),
3321 (b'u', b'user', None, _(b'list the author (long with -v)')),
3322 (b'd', b'date', None, _(b'list the date (short with -q)')),
3322 (b'd', b'date', None, _(b'list the date (short with -q)')),
3323 ]
3323 ]
3324 + formatteropts
3324 + formatteropts
3325 + walkopts,
3325 + walkopts,
3326 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3326 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3327 helpcategory=command.CATEGORY_FILE_CONTENTS,
3327 helpcategory=command.CATEGORY_FILE_CONTENTS,
3328 inferrepo=True,
3328 inferrepo=True,
3329 intents={INTENT_READONLY},
3329 intents={INTENT_READONLY},
3330 )
3330 )
3331 def grep(ui, repo, pattern, *pats, **opts):
3331 def grep(ui, repo, pattern, *pats, **opts):
3332 """search for a pattern in specified files
3332 """search for a pattern in specified files
3333
3333
3334 Search the working directory or revision history for a regular
3334 Search the working directory or revision history for a regular
3335 expression in the specified files for the entire repository.
3335 expression in the specified files for the entire repository.
3336
3336
3337 By default, grep searches the repository files in the working
3337 By default, grep searches the repository files in the working
3338 directory and prints the files where it finds a match. To specify
3338 directory and prints the files where it finds a match. To specify
3339 historical revisions instead of the working directory, use the
3339 historical revisions instead of the working directory, use the
3340 --rev flag.
3340 --rev flag.
3341
3341
3342 To search instead historical revision differences that contains a
3342 To search instead historical revision differences that contains a
3343 change in match status ("-" for a match that becomes a non-match,
3343 change in match status ("-" for a match that becomes a non-match,
3344 or "+" for a non-match that becomes a match), use the --diff flag.
3344 or "+" for a non-match that becomes a match), use the --diff flag.
3345
3345
3346 PATTERN can be any Python (roughly Perl-compatible) regular
3346 PATTERN can be any Python (roughly Perl-compatible) regular
3347 expression.
3347 expression.
3348
3348
3349 If no FILEs are specified and the --rev flag isn't supplied, all
3349 If no FILEs are specified and the --rev flag isn't supplied, all
3350 files in the working directory are searched. When using the --rev
3350 files in the working directory are searched. When using the --rev
3351 flag and specifying FILEs, use the --follow argument to also
3351 flag and specifying FILEs, use the --follow argument to also
3352 follow the specified FILEs across renames and copies.
3352 follow the specified FILEs across renames and copies.
3353
3353
3354 .. container:: verbose
3354 .. container:: verbose
3355
3355
3356 Template:
3356 Template:
3357
3357
3358 The following keywords are supported in addition to the common template
3358 The following keywords are supported in addition to the common template
3359 keywords and functions. See also :hg:`help templates`.
3359 keywords and functions. See also :hg:`help templates`.
3360
3360
3361 :change: String. Character denoting insertion ``+`` or removal ``-``.
3361 :change: String. Character denoting insertion ``+`` or removal ``-``.
3362 Available if ``--diff`` is specified.
3362 Available if ``--diff`` is specified.
3363 :lineno: Integer. Line number of the match.
3363 :lineno: Integer. Line number of the match.
3364 :path: String. Repository-absolute path of the file.
3364 :path: String. Repository-absolute path of the file.
3365 :texts: List of text chunks.
3365 :texts: List of text chunks.
3366
3366
3367 And each entry of ``{texts}`` provides the following sub-keywords.
3367 And each entry of ``{texts}`` provides the following sub-keywords.
3368
3368
3369 :matched: Boolean. True if the chunk matches the specified pattern.
3369 :matched: Boolean. True if the chunk matches the specified pattern.
3370 :text: String. Chunk content.
3370 :text: String. Chunk content.
3371
3371
3372 See :hg:`help templates.operators` for the list expansion syntax.
3372 See :hg:`help templates.operators` for the list expansion syntax.
3373
3373
3374 Returns 0 if a match is found, 1 otherwise.
3374 Returns 0 if a match is found, 1 otherwise.
3375
3375
3376 """
3376 """
3377 opts = pycompat.byteskwargs(opts)
3377 opts = pycompat.byteskwargs(opts)
3378 diff = opts.get(b'all') or opts.get(b'diff')
3378 diff = opts.get(b'all') or opts.get(b'diff')
3379 if diff and opts.get(b'all_files'):
3379 if diff and opts.get(b'all_files'):
3380 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3380 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3381 if opts.get(b'all_files') is None and not diff:
3381 if opts.get(b'all_files') is None and not diff:
3382 opts[b'all_files'] = True
3382 opts[b'all_files'] = True
3383 plaingrep = (
3383 plaingrep = (
3384 opts.get(b'all_files')
3384 opts.get(b'all_files')
3385 and not opts.get(b'rev')
3385 and not opts.get(b'rev')
3386 and not opts.get(b'follow')
3386 and not opts.get(b'follow')
3387 )
3387 )
3388 all_files = opts.get(b'all_files')
3388 all_files = opts.get(b'all_files')
3389 if plaingrep:
3389 if plaingrep:
3390 opts[b'rev'] = [b'wdir()']
3390 opts[b'rev'] = [b'wdir()']
3391
3391
3392 reflags = re.M
3392 reflags = re.M
3393 if opts.get(b'ignore_case'):
3393 if opts.get(b'ignore_case'):
3394 reflags |= re.I
3394 reflags |= re.I
3395 try:
3395 try:
3396 regexp = util.re.compile(pattern, reflags)
3396 regexp = util.re.compile(pattern, reflags)
3397 except re.error as inst:
3397 except re.error as inst:
3398 ui.warn(
3398 ui.warn(
3399 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3399 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3400 )
3400 )
3401 return 1
3401 return 1
3402 sep, eol = b':', b'\n'
3402 sep, eol = b':', b'\n'
3403 if opts.get(b'print0'):
3403 if opts.get(b'print0'):
3404 sep = eol = b'\0'
3404 sep = eol = b'\0'
3405
3405
3406 getfile = util.lrucachefunc(repo.file)
3406 getfile = util.lrucachefunc(repo.file)
3407
3407
3408 def matchlines(body):
3408 def matchlines(body):
3409 begin = 0
3409 begin = 0
3410 linenum = 0
3410 linenum = 0
3411 while begin < len(body):
3411 while begin < len(body):
3412 match = regexp.search(body, begin)
3412 match = regexp.search(body, begin)
3413 if not match:
3413 if not match:
3414 break
3414 break
3415 mstart, mend = match.span()
3415 mstart, mend = match.span()
3416 linenum += body.count(b'\n', begin, mstart) + 1
3416 linenum += body.count(b'\n', begin, mstart) + 1
3417 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3417 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3418 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3418 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3419 lend = begin - 1
3419 lend = begin - 1
3420 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3420 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3421
3421
3422 class linestate(object):
3422 class linestate(object):
3423 def __init__(self, line, linenum, colstart, colend):
3423 def __init__(self, line, linenum, colstart, colend):
3424 self.line = line
3424 self.line = line
3425 self.linenum = linenum
3425 self.linenum = linenum
3426 self.colstart = colstart
3426 self.colstart = colstart
3427 self.colend = colend
3427 self.colend = colend
3428
3428
3429 def __hash__(self):
3429 def __hash__(self):
3430 return hash(self.line)
3430 return hash(self.line)
3431
3431
3432 def __eq__(self, other):
3432 def __eq__(self, other):
3433 return self.line == other.line
3433 return self.line == other.line
3434
3434
3435 def findpos(self):
3435 def findpos(self):
3436 """Iterate all (start, end) indices of matches"""
3436 """Iterate all (start, end) indices of matches"""
3437 yield self.colstart, self.colend
3437 yield self.colstart, self.colend
3438 p = self.colend
3438 p = self.colend
3439 while p < len(self.line):
3439 while p < len(self.line):
3440 m = regexp.search(self.line, p)
3440 m = regexp.search(self.line, p)
3441 if not m:
3441 if not m:
3442 break
3442 break
3443 if m.end() == p:
3443 if m.end() == p:
3444 p += 1
3444 p += 1
3445 else:
3445 else:
3446 yield m.span()
3446 yield m.span()
3447 p = m.end()
3447 p = m.end()
3448
3448
3449 matches = {}
3449 matches = {}
3450 copies = {}
3450 copies = {}
3451
3451
3452 def grepbody(fn, rev, body):
3452 def grepbody(fn, rev, body):
3453 matches[rev].setdefault(fn, [])
3453 matches[rev].setdefault(fn, [])
3454 m = matches[rev][fn]
3454 m = matches[rev][fn]
3455 if body is None:
3455 if body is None:
3456 return
3456 return
3457
3457
3458 for lnum, cstart, cend, line in matchlines(body):
3458 for lnum, cstart, cend, line in matchlines(body):
3459 s = linestate(line, lnum, cstart, cend)
3459 s = linestate(line, lnum, cstart, cend)
3460 m.append(s)
3460 m.append(s)
3461
3461
3462 def difflinestates(a, b):
3462 def difflinestates(a, b):
3463 sm = difflib.SequenceMatcher(None, a, b)
3463 sm = difflib.SequenceMatcher(None, a, b)
3464 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3464 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3465 if tag == 'insert':
3465 if tag == 'insert':
3466 for i in pycompat.xrange(blo, bhi):
3466 for i in pycompat.xrange(blo, bhi):
3467 yield (b'+', b[i])
3467 yield (b'+', b[i])
3468 elif tag == 'delete':
3468 elif tag == 'delete':
3469 for i in pycompat.xrange(alo, ahi):
3469 for i in pycompat.xrange(alo, ahi):
3470 yield (b'-', a[i])
3470 yield (b'-', a[i])
3471 elif tag == 'replace':
3471 elif tag == 'replace':
3472 for i in pycompat.xrange(alo, ahi):
3472 for i in pycompat.xrange(alo, ahi):
3473 yield (b'-', a[i])
3473 yield (b'-', a[i])
3474 for i in pycompat.xrange(blo, bhi):
3474 for i in pycompat.xrange(blo, bhi):
3475 yield (b'+', b[i])
3475 yield (b'+', b[i])
3476
3476
3477 uipathfn = scmutil.getuipathfn(repo)
3477 uipathfn = scmutil.getuipathfn(repo)
3478
3478
3479 def display(fm, fn, ctx, pstates, states):
3479 def display(fm, fn, ctx, pstates, states):
3480 rev = scmutil.intrev(ctx)
3480 rev = scmutil.intrev(ctx)
3481 if fm.isplain():
3481 if fm.isplain():
3482 formatuser = ui.shortuser
3482 formatuser = ui.shortuser
3483 else:
3483 else:
3484 formatuser = pycompat.bytestr
3484 formatuser = pycompat.bytestr
3485 if ui.quiet:
3485 if ui.quiet:
3486 datefmt = b'%Y-%m-%d'
3486 datefmt = b'%Y-%m-%d'
3487 else:
3487 else:
3488 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3488 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3489 found = False
3489 found = False
3490
3490
3491 @util.cachefunc
3491 @util.cachefunc
3492 def binary():
3492 def binary():
3493 flog = getfile(fn)
3493 flog = getfile(fn)
3494 try:
3494 try:
3495 return stringutil.binary(flog.read(ctx.filenode(fn)))
3495 return stringutil.binary(flog.read(ctx.filenode(fn)))
3496 except error.WdirUnsupported:
3496 except error.WdirUnsupported:
3497 return ctx[fn].isbinary()
3497 return ctx[fn].isbinary()
3498
3498
3499 fieldnamemap = {b'linenumber': b'lineno'}
3499 fieldnamemap = {b'linenumber': b'lineno'}
3500 if diff:
3500 if diff:
3501 iter = difflinestates(pstates, states)
3501 iter = difflinestates(pstates, states)
3502 else:
3502 else:
3503 iter = [(b'', l) for l in states]
3503 iter = [(b'', l) for l in states]
3504 for change, l in iter:
3504 for change, l in iter:
3505 fm.startitem()
3505 fm.startitem()
3506 fm.context(ctx=ctx)
3506 fm.context(ctx=ctx)
3507 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3507 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3508 fm.plain(uipathfn(fn), label=b'grep.filename')
3508 fm.plain(uipathfn(fn), label=b'grep.filename')
3509
3509
3510 cols = [
3510 cols = [
3511 (b'rev', b'%d', rev, not plaingrep, b''),
3511 (b'rev', b'%d', rev, not plaingrep, b''),
3512 (
3512 (
3513 b'linenumber',
3513 b'linenumber',
3514 b'%d',
3514 b'%d',
3515 l.linenum,
3515 l.linenum,
3516 opts.get(b'line_number'),
3516 opts.get(b'line_number'),
3517 b'',
3517 b'',
3518 ),
3518 ),
3519 ]
3519 ]
3520 if diff:
3520 if diff:
3521 cols.append(
3521 cols.append(
3522 (
3522 (
3523 b'change',
3523 b'change',
3524 b'%s',
3524 b'%s',
3525 change,
3525 change,
3526 True,
3526 True,
3527 b'grep.inserted '
3527 b'grep.inserted '
3528 if change == b'+'
3528 if change == b'+'
3529 else b'grep.deleted ',
3529 else b'grep.deleted ',
3530 )
3530 )
3531 )
3531 )
3532 cols.extend(
3532 cols.extend(
3533 [
3533 [
3534 (
3534 (
3535 b'user',
3535 b'user',
3536 b'%s',
3536 b'%s',
3537 formatuser(ctx.user()),
3537 formatuser(ctx.user()),
3538 opts.get(b'user'),
3538 opts.get(b'user'),
3539 b'',
3539 b'',
3540 ),
3540 ),
3541 (
3541 (
3542 b'date',
3542 b'date',
3543 b'%s',
3543 b'%s',
3544 fm.formatdate(ctx.date(), datefmt),
3544 fm.formatdate(ctx.date(), datefmt),
3545 opts.get(b'date'),
3545 opts.get(b'date'),
3546 b'',
3546 b'',
3547 ),
3547 ),
3548 ]
3548 ]
3549 )
3549 )
3550 for name, fmt, data, cond, extra_label in cols:
3550 for name, fmt, data, cond, extra_label in cols:
3551 if cond:
3551 if cond:
3552 fm.plain(sep, label=b'grep.sep')
3552 fm.plain(sep, label=b'grep.sep')
3553 field = fieldnamemap.get(name, name)
3553 field = fieldnamemap.get(name, name)
3554 label = extra_label + (b'grep.%s' % name)
3554 label = extra_label + (b'grep.%s' % name)
3555 fm.condwrite(cond, field, fmt, data, label=label)
3555 fm.condwrite(cond, field, fmt, data, label=label)
3556 if not opts.get(b'files_with_matches'):
3556 if not opts.get(b'files_with_matches'):
3557 fm.plain(sep, label=b'grep.sep')
3557 fm.plain(sep, label=b'grep.sep')
3558 if not opts.get(b'text') and binary():
3558 if not opts.get(b'text') and binary():
3559 fm.plain(_(b" Binary file matches"))
3559 fm.plain(_(b" Binary file matches"))
3560 else:
3560 else:
3561 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3561 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3562 fm.plain(eol)
3562 fm.plain(eol)
3563 found = True
3563 found = True
3564 if opts.get(b'files_with_matches'):
3564 if opts.get(b'files_with_matches'):
3565 break
3565 break
3566 return found
3566 return found
3567
3567
3568 def displaymatches(fm, l):
3568 def displaymatches(fm, l):
3569 p = 0
3569 p = 0
3570 for s, e in l.findpos():
3570 for s, e in l.findpos():
3571 if p < s:
3571 if p < s:
3572 fm.startitem()
3572 fm.startitem()
3573 fm.write(b'text', b'%s', l.line[p:s])
3573 fm.write(b'text', b'%s', l.line[p:s])
3574 fm.data(matched=False)
3574 fm.data(matched=False)
3575 fm.startitem()
3575 fm.startitem()
3576 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3576 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3577 fm.data(matched=True)
3577 fm.data(matched=True)
3578 p = e
3578 p = e
3579 if p < len(l.line):
3579 if p < len(l.line):
3580 fm.startitem()
3580 fm.startitem()
3581 fm.write(b'text', b'%s', l.line[p:])
3581 fm.write(b'text', b'%s', l.line[p:])
3582 fm.data(matched=False)
3582 fm.data(matched=False)
3583 fm.end()
3583 fm.end()
3584
3584
3585 skip = set()
3585 skip = set()
3586 revfiles = {}
3586 revfiles = {}
3587 match = scmutil.match(repo[None], pats, opts)
3587 match = scmutil.match(repo[None], pats, opts)
3588 found = False
3588 found = False
3589 follow = opts.get(b'follow')
3589 follow = opts.get(b'follow')
3590
3590
3591 getrenamed = scmutil.getrenamedfn(repo)
3591 getrenamed = scmutil.getrenamedfn(repo)
3592
3592
3593 def readfile(ctx, fn):
3593 def readfile(ctx, fn):
3594 rev = ctx.rev()
3594 rev = ctx.rev()
3595 if rev is None:
3595 if rev is None:
3596 fctx = ctx[fn]
3596 fctx = ctx[fn]
3597 try:
3597 try:
3598 return fctx.data()
3598 return fctx.data()
3599 except IOError as e:
3599 except IOError as e:
3600 if e.errno != errno.ENOENT:
3600 if e.errno != errno.ENOENT:
3601 raise
3601 raise
3602 else:
3602 else:
3603 flog = getfile(fn)
3603 flog = getfile(fn)
3604 fnode = ctx.filenode(fn)
3604 fnode = ctx.filenode(fn)
3605 try:
3605 try:
3606 return flog.read(fnode)
3606 return flog.read(fnode)
3607 except error.CensoredNodeError:
3607 except error.CensoredNodeError:
3608 ui.warn(
3608 ui.warn(
3609 _(
3609 _(
3610 b'cannot search in censored file: %(filename)s:%(revnum)s\n'
3610 b'cannot search in censored file: %(filename)s:%(revnum)s\n'
3611 )
3611 )
3612 % {b'filename': fn, b'revnum': pycompat.bytestr(rev),}
3612 % {b'filename': fn, b'revnum': pycompat.bytestr(rev),}
3613 )
3613 )
3614
3614
3615 def prep(ctx, fns):
3615 def prep(ctx, fns):
3616 rev = ctx.rev()
3616 rev = ctx.rev()
3617 pctx = ctx.p1()
3617 pctx = ctx.p1()
3618 matches.setdefault(rev, {})
3618 matches.setdefault(rev, {})
3619 if diff:
3619 if diff:
3620 parent = pctx.rev()
3620 parent = pctx.rev()
3621 matches.setdefault(parent, {})
3621 matches.setdefault(parent, {})
3622 files = revfiles.setdefault(rev, [])
3622 files = revfiles.setdefault(rev, [])
3623 if rev is None:
3623 if rev is None:
3624 # in `hg grep pattern`, 2/3 of the time is spent is spent in
3624 # in `hg grep pattern`, 2/3 of the time is spent is spent in
3625 # pathauditor checks without this in mozilla-central
3625 # pathauditor checks without this in mozilla-central
3626 contextmanager = repo.wvfs.audit.cached
3626 contextmanager = repo.wvfs.audit.cached
3627 else:
3627 else:
3628 contextmanager = util.nullcontextmanager
3628 contextmanager = util.nullcontextmanager
3629 with contextmanager():
3629 with contextmanager():
3630 for fn in fns:
3630 for fn in fns:
3631 # fn might not exist in the revision (could be a file removed by
3631 # fn might not exist in the revision (could be a file removed by
3632 # the revision). We could check `fn not in ctx` even when rev is
3632 # the revision). We could check `fn not in ctx` even when rev is
3633 # None, but it's less racy to protect againt that in readfile.
3633 # None, but it's less racy to protect againt that in readfile.
3634 if rev is not None and fn not in ctx:
3634 if rev is not None and fn not in ctx:
3635 continue
3635 continue
3636
3636
3637 copy = None
3637 copy = None
3638 if follow:
3638 if follow:
3639 copy = getrenamed(fn, rev)
3639 copy = getrenamed(fn, rev)
3640 if copy:
3640 if copy:
3641 copies.setdefault(rev, {})[fn] = copy
3641 copies.setdefault(rev, {})[fn] = copy
3642 if fn in skip:
3642 if fn in skip:
3643 skip.add(copy)
3643 skip.add(copy)
3644 if fn in skip:
3644 if fn in skip:
3645 continue
3645 continue
3646 files.append(fn)
3646 files.append(fn)
3647
3647
3648 if fn not in matches[rev]:
3648 if fn not in matches[rev]:
3649 grepbody(fn, rev, readfile(ctx, fn))
3649 grepbody(fn, rev, readfile(ctx, fn))
3650
3650
3651 if diff:
3651 if diff:
3652 pfn = copy or fn
3652 pfn = copy or fn
3653 if pfn not in matches[parent] and pfn in pctx:
3653 if pfn not in matches[parent] and pfn in pctx:
3654 grepbody(pfn, parent, readfile(pctx, pfn))
3654 grepbody(pfn, parent, readfile(pctx, pfn))
3655
3655
3656 ui.pager(b'grep')
3656 ui.pager(b'grep')
3657 fm = ui.formatter(b'grep', opts)
3657 fm = ui.formatter(b'grep', opts)
3658 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3658 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3659 rev = ctx.rev()
3659 rev = ctx.rev()
3660 parent = ctx.p1().rev()
3660 parent = ctx.p1().rev()
3661 for fn in sorted(revfiles.get(rev, [])):
3661 for fn in sorted(revfiles.get(rev, [])):
3662 states = matches[rev][fn]
3662 states = matches[rev][fn]
3663 copy = copies.get(rev, {}).get(fn)
3663 copy = copies.get(rev, {}).get(fn)
3664 if fn in skip:
3664 if fn in skip:
3665 if copy:
3665 if copy:
3666 skip.add(copy)
3666 skip.add(copy)
3667 continue
3667 continue
3668 pstates = matches.get(parent, {}).get(copy or fn, [])
3668 pstates = matches.get(parent, {}).get(copy or fn, [])
3669 if pstates or states:
3669 if pstates or states:
3670 r = display(fm, fn, ctx, pstates, states)
3670 r = display(fm, fn, ctx, pstates, states)
3671 found = found or r
3671 found = found or r
3672 if r and not diff and not all_files:
3672 if r and not diff and not all_files:
3673 skip.add(fn)
3673 skip.add(fn)
3674 if copy:
3674 if copy:
3675 skip.add(copy)
3675 skip.add(copy)
3676 del revfiles[rev]
3676 del revfiles[rev]
3677 # We will keep the matches dict for the duration of the window
3677 # We will keep the matches dict for the duration of the window
3678 # clear the matches dict once the window is over
3678 # clear the matches dict once the window is over
3679 if not revfiles:
3679 if not revfiles:
3680 matches.clear()
3680 matches.clear()
3681 fm.end()
3681 fm.end()
3682
3682
3683 return not found
3683 return not found
3684
3684
3685
3685
3686 @command(
3686 @command(
3687 b'heads',
3687 b'heads',
3688 [
3688 [
3689 (
3689 (
3690 b'r',
3690 b'r',
3691 b'rev',
3691 b'rev',
3692 b'',
3692 b'',
3693 _(b'show only heads which are descendants of STARTREV'),
3693 _(b'show only heads which are descendants of STARTREV'),
3694 _(b'STARTREV'),
3694 _(b'STARTREV'),
3695 ),
3695 ),
3696 (b't', b'topo', False, _(b'show topological heads only')),
3696 (b't', b'topo', False, _(b'show topological heads only')),
3697 (
3697 (
3698 b'a',
3698 b'a',
3699 b'active',
3699 b'active',
3700 False,
3700 False,
3701 _(b'show active branchheads only (DEPRECATED)'),
3701 _(b'show active branchheads only (DEPRECATED)'),
3702 ),
3702 ),
3703 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3703 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3704 ]
3704 ]
3705 + templateopts,
3705 + templateopts,
3706 _(b'[-ct] [-r STARTREV] [REV]...'),
3706 _(b'[-ct] [-r STARTREV] [REV]...'),
3707 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3707 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3708 intents={INTENT_READONLY},
3708 intents={INTENT_READONLY},
3709 )
3709 )
3710 def heads(ui, repo, *branchrevs, **opts):
3710 def heads(ui, repo, *branchrevs, **opts):
3711 """show branch heads
3711 """show branch heads
3712
3712
3713 With no arguments, show all open branch heads in the repository.
3713 With no arguments, show all open branch heads in the repository.
3714 Branch heads are changesets that have no descendants on the
3714 Branch heads are changesets that have no descendants on the
3715 same branch. They are where development generally takes place and
3715 same branch. They are where development generally takes place and
3716 are the usual targets for update and merge operations.
3716 are the usual targets for update and merge operations.
3717
3717
3718 If one or more REVs are given, only open branch heads on the
3718 If one or more REVs are given, only open branch heads on the
3719 branches associated with the specified changesets are shown. This
3719 branches associated with the specified changesets are shown. This
3720 means that you can use :hg:`heads .` to see the heads on the
3720 means that you can use :hg:`heads .` to see the heads on the
3721 currently checked-out branch.
3721 currently checked-out branch.
3722
3722
3723 If -c/--closed is specified, also show branch heads marked closed
3723 If -c/--closed is specified, also show branch heads marked closed
3724 (see :hg:`commit --close-branch`).
3724 (see :hg:`commit --close-branch`).
3725
3725
3726 If STARTREV is specified, only those heads that are descendants of
3726 If STARTREV is specified, only those heads that are descendants of
3727 STARTREV will be displayed.
3727 STARTREV will be displayed.
3728
3728
3729 If -t/--topo is specified, named branch mechanics will be ignored and only
3729 If -t/--topo is specified, named branch mechanics will be ignored and only
3730 topological heads (changesets with no children) will be shown.
3730 topological heads (changesets with no children) will be shown.
3731
3731
3732 Returns 0 if matching heads are found, 1 if not.
3732 Returns 0 if matching heads are found, 1 if not.
3733 """
3733 """
3734
3734
3735 opts = pycompat.byteskwargs(opts)
3735 opts = pycompat.byteskwargs(opts)
3736 start = None
3736 start = None
3737 rev = opts.get(b'rev')
3737 rev = opts.get(b'rev')
3738 if rev:
3738 if rev:
3739 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3739 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3740 start = scmutil.revsingle(repo, rev, None).node()
3740 start = scmutil.revsingle(repo, rev, None).node()
3741
3741
3742 if opts.get(b'topo'):
3742 if opts.get(b'topo'):
3743 heads = [repo[h] for h in repo.heads(start)]
3743 heads = [repo[h] for h in repo.heads(start)]
3744 else:
3744 else:
3745 heads = []
3745 heads = []
3746 for branch in repo.branchmap():
3746 for branch in repo.branchmap():
3747 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3747 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3748 heads = [repo[h] for h in heads]
3748 heads = [repo[h] for h in heads]
3749
3749
3750 if branchrevs:
3750 if branchrevs:
3751 branches = {
3751 branches = {
3752 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3752 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3753 }
3753 }
3754 heads = [h for h in heads if h.branch() in branches]
3754 heads = [h for h in heads if h.branch() in branches]
3755
3755
3756 if opts.get(b'active') and branchrevs:
3756 if opts.get(b'active') and branchrevs:
3757 dagheads = repo.heads(start)
3757 dagheads = repo.heads(start)
3758 heads = [h for h in heads if h.node() in dagheads]
3758 heads = [h for h in heads if h.node() in dagheads]
3759
3759
3760 if branchrevs:
3760 if branchrevs:
3761 haveheads = {h.branch() for h in heads}
3761 haveheads = {h.branch() for h in heads}
3762 if branches - haveheads:
3762 if branches - haveheads:
3763 headless = b', '.join(b for b in branches - haveheads)
3763 headless = b', '.join(b for b in branches - haveheads)
3764 msg = _(b'no open branch heads found on branches %s')
3764 msg = _(b'no open branch heads found on branches %s')
3765 if opts.get(b'rev'):
3765 if opts.get(b'rev'):
3766 msg += _(b' (started at %s)') % opts[b'rev']
3766 msg += _(b' (started at %s)') % opts[b'rev']
3767 ui.warn((msg + b'\n') % headless)
3767 ui.warn((msg + b'\n') % headless)
3768
3768
3769 if not heads:
3769 if not heads:
3770 return 1
3770 return 1
3771
3771
3772 ui.pager(b'heads')
3772 ui.pager(b'heads')
3773 heads = sorted(heads, key=lambda x: -(x.rev()))
3773 heads = sorted(heads, key=lambda x: -(x.rev()))
3774 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3774 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3775 for ctx in heads:
3775 for ctx in heads:
3776 displayer.show(ctx)
3776 displayer.show(ctx)
3777 displayer.close()
3777 displayer.close()
3778
3778
3779
3779
3780 @command(
3780 @command(
3781 b'help',
3781 b'help',
3782 [
3782 [
3783 (b'e', b'extension', None, _(b'show only help for extensions')),
3783 (b'e', b'extension', None, _(b'show only help for extensions')),
3784 (b'c', b'command', None, _(b'show only help for commands')),
3784 (b'c', b'command', None, _(b'show only help for commands')),
3785 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3785 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3786 (
3786 (
3787 b's',
3787 b's',
3788 b'system',
3788 b'system',
3789 [],
3789 [],
3790 _(b'show help for specific platform(s)'),
3790 _(b'show help for specific platform(s)'),
3791 _(b'PLATFORM'),
3791 _(b'PLATFORM'),
3792 ),
3792 ),
3793 ],
3793 ],
3794 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3794 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3795 helpcategory=command.CATEGORY_HELP,
3795 helpcategory=command.CATEGORY_HELP,
3796 norepo=True,
3796 norepo=True,
3797 intents={INTENT_READONLY},
3797 intents={INTENT_READONLY},
3798 )
3798 )
3799 def help_(ui, name=None, **opts):
3799 def help_(ui, name=None, **opts):
3800 """show help for a given topic or a help overview
3800 """show help for a given topic or a help overview
3801
3801
3802 With no arguments, print a list of commands with short help messages.
3802 With no arguments, print a list of commands with short help messages.
3803
3803
3804 Given a topic, extension, or command name, print help for that
3804 Given a topic, extension, or command name, print help for that
3805 topic.
3805 topic.
3806
3806
3807 Returns 0 if successful.
3807 Returns 0 if successful.
3808 """
3808 """
3809
3809
3810 keep = opts.get('system') or []
3810 keep = opts.get('system') or []
3811 if len(keep) == 0:
3811 if len(keep) == 0:
3812 if pycompat.sysplatform.startswith(b'win'):
3812 if pycompat.sysplatform.startswith(b'win'):
3813 keep.append(b'windows')
3813 keep.append(b'windows')
3814 elif pycompat.sysplatform == b'OpenVMS':
3814 elif pycompat.sysplatform == b'OpenVMS':
3815 keep.append(b'vms')
3815 keep.append(b'vms')
3816 elif pycompat.sysplatform == b'plan9':
3816 elif pycompat.sysplatform == b'plan9':
3817 keep.append(b'plan9')
3817 keep.append(b'plan9')
3818 else:
3818 else:
3819 keep.append(b'unix')
3819 keep.append(b'unix')
3820 keep.append(pycompat.sysplatform.lower())
3820 keep.append(pycompat.sysplatform.lower())
3821 if ui.verbose:
3821 if ui.verbose:
3822 keep.append(b'verbose')
3822 keep.append(b'verbose')
3823
3823
3824 commands = sys.modules[__name__]
3824 commands = sys.modules[__name__]
3825 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3825 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3826 ui.pager(b'help')
3826 ui.pager(b'help')
3827 ui.write(formatted)
3827 ui.write(formatted)
3828
3828
3829
3829
3830 @command(
3830 @command(
3831 b'identify|id',
3831 b'identify|id',
3832 [
3832 [
3833 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3833 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3834 (b'n', b'num', None, _(b'show local revision number')),
3834 (b'n', b'num', None, _(b'show local revision number')),
3835 (b'i', b'id', None, _(b'show global revision id')),
3835 (b'i', b'id', None, _(b'show global revision id')),
3836 (b'b', b'branch', None, _(b'show branch')),
3836 (b'b', b'branch', None, _(b'show branch')),
3837 (b't', b'tags', None, _(b'show tags')),
3837 (b't', b'tags', None, _(b'show tags')),
3838 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3838 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3839 ]
3839 ]
3840 + remoteopts
3840 + remoteopts
3841 + formatteropts,
3841 + formatteropts,
3842 _(b'[-nibtB] [-r REV] [SOURCE]'),
3842 _(b'[-nibtB] [-r REV] [SOURCE]'),
3843 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3843 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3844 optionalrepo=True,
3844 optionalrepo=True,
3845 intents={INTENT_READONLY},
3845 intents={INTENT_READONLY},
3846 )
3846 )
3847 def identify(
3847 def identify(
3848 ui,
3848 ui,
3849 repo,
3849 repo,
3850 source=None,
3850 source=None,
3851 rev=None,
3851 rev=None,
3852 num=None,
3852 num=None,
3853 id=None,
3853 id=None,
3854 branch=None,
3854 branch=None,
3855 tags=None,
3855 tags=None,
3856 bookmarks=None,
3856 bookmarks=None,
3857 **opts
3857 **opts
3858 ):
3858 ):
3859 """identify the working directory or specified revision
3859 """identify the working directory or specified revision
3860
3860
3861 Print a summary identifying the repository state at REV using one or
3861 Print a summary identifying the repository state at REV using one or
3862 two parent hash identifiers, followed by a "+" if the working
3862 two parent hash identifiers, followed by a "+" if the working
3863 directory has uncommitted changes, the branch name (if not default),
3863 directory has uncommitted changes, the branch name (if not default),
3864 a list of tags, and a list of bookmarks.
3864 a list of tags, and a list of bookmarks.
3865
3865
3866 When REV is not given, print a summary of the current state of the
3866 When REV is not given, print a summary of the current state of the
3867 repository including the working directory. Specify -r. to get information
3867 repository including the working directory. Specify -r. to get information
3868 of the working directory parent without scanning uncommitted changes.
3868 of the working directory parent without scanning uncommitted changes.
3869
3869
3870 Specifying a path to a repository root or Mercurial bundle will
3870 Specifying a path to a repository root or Mercurial bundle will
3871 cause lookup to operate on that repository/bundle.
3871 cause lookup to operate on that repository/bundle.
3872
3872
3873 .. container:: verbose
3873 .. container:: verbose
3874
3874
3875 Template:
3875 Template:
3876
3876
3877 The following keywords are supported in addition to the common template
3877 The following keywords are supported in addition to the common template
3878 keywords and functions. See also :hg:`help templates`.
3878 keywords and functions. See also :hg:`help templates`.
3879
3879
3880 :dirty: String. Character ``+`` denoting if the working directory has
3880 :dirty: String. Character ``+`` denoting if the working directory has
3881 uncommitted changes.
3881 uncommitted changes.
3882 :id: String. One or two nodes, optionally followed by ``+``.
3882 :id: String. One or two nodes, optionally followed by ``+``.
3883 :parents: List of strings. Parent nodes of the changeset.
3883 :parents: List of strings. Parent nodes of the changeset.
3884
3884
3885 Examples:
3885 Examples:
3886
3886
3887 - generate a build identifier for the working directory::
3887 - generate a build identifier for the working directory::
3888
3888
3889 hg id --id > build-id.dat
3889 hg id --id > build-id.dat
3890
3890
3891 - find the revision corresponding to a tag::
3891 - find the revision corresponding to a tag::
3892
3892
3893 hg id -n -r 1.3
3893 hg id -n -r 1.3
3894
3894
3895 - check the most recent revision of a remote repository::
3895 - check the most recent revision of a remote repository::
3896
3896
3897 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3897 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3898
3898
3899 See :hg:`log` for generating more information about specific revisions,
3899 See :hg:`log` for generating more information about specific revisions,
3900 including full hash identifiers.
3900 including full hash identifiers.
3901
3901
3902 Returns 0 if successful.
3902 Returns 0 if successful.
3903 """
3903 """
3904
3904
3905 opts = pycompat.byteskwargs(opts)
3905 opts = pycompat.byteskwargs(opts)
3906 if not repo and not source:
3906 if not repo and not source:
3907 raise error.Abort(
3907 raise error.Abort(
3908 _(b"there is no Mercurial repository here (.hg not found)")
3908 _(b"there is no Mercurial repository here (.hg not found)")
3909 )
3909 )
3910
3910
3911 default = not (num or id or branch or tags or bookmarks)
3911 default = not (num or id or branch or tags or bookmarks)
3912 output = []
3912 output = []
3913 revs = []
3913 revs = []
3914
3914
3915 if source:
3915 if source:
3916 source, branches = hg.parseurl(ui.expandpath(source))
3916 source, branches = hg.parseurl(ui.expandpath(source))
3917 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3917 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3918 repo = peer.local()
3918 repo = peer.local()
3919 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3919 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3920
3920
3921 fm = ui.formatter(b'identify', opts)
3921 fm = ui.formatter(b'identify', opts)
3922 fm.startitem()
3922 fm.startitem()
3923
3923
3924 if not repo:
3924 if not repo:
3925 if num or branch or tags:
3925 if num or branch or tags:
3926 raise error.Abort(
3926 raise error.Abort(
3927 _(b"can't query remote revision number, branch, or tags")
3927 _(b"can't query remote revision number, branch, or tags")
3928 )
3928 )
3929 if not rev and revs:
3929 if not rev and revs:
3930 rev = revs[0]
3930 rev = revs[0]
3931 if not rev:
3931 if not rev:
3932 rev = b"tip"
3932 rev = b"tip"
3933
3933
3934 remoterev = peer.lookup(rev)
3934 remoterev = peer.lookup(rev)
3935 hexrev = fm.hexfunc(remoterev)
3935 hexrev = fm.hexfunc(remoterev)
3936 if default or id:
3936 if default or id:
3937 output = [hexrev]
3937 output = [hexrev]
3938 fm.data(id=hexrev)
3938 fm.data(id=hexrev)
3939
3939
3940 @util.cachefunc
3940 @util.cachefunc
3941 def getbms():
3941 def getbms():
3942 bms = []
3942 bms = []
3943
3943
3944 if b'bookmarks' in peer.listkeys(b'namespaces'):
3944 if b'bookmarks' in peer.listkeys(b'namespaces'):
3945 hexremoterev = hex(remoterev)
3945 hexremoterev = hex(remoterev)
3946 bms = [
3946 bms = [
3947 bm
3947 bm
3948 for bm, bmr in pycompat.iteritems(
3948 for bm, bmr in pycompat.iteritems(
3949 peer.listkeys(b'bookmarks')
3949 peer.listkeys(b'bookmarks')
3950 )
3950 )
3951 if bmr == hexremoterev
3951 if bmr == hexremoterev
3952 ]
3952 ]
3953
3953
3954 return sorted(bms)
3954 return sorted(bms)
3955
3955
3956 if fm.isplain():
3956 if fm.isplain():
3957 if bookmarks:
3957 if bookmarks:
3958 output.extend(getbms())
3958 output.extend(getbms())
3959 elif default and not ui.quiet:
3959 elif default and not ui.quiet:
3960 # multiple bookmarks for a single parent separated by '/'
3960 # multiple bookmarks for a single parent separated by '/'
3961 bm = b'/'.join(getbms())
3961 bm = b'/'.join(getbms())
3962 if bm:
3962 if bm:
3963 output.append(bm)
3963 output.append(bm)
3964 else:
3964 else:
3965 fm.data(node=hex(remoterev))
3965 fm.data(node=hex(remoterev))
3966 if bookmarks or b'bookmarks' in fm.datahint():
3966 if bookmarks or b'bookmarks' in fm.datahint():
3967 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3967 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3968 else:
3968 else:
3969 if rev:
3969 if rev:
3970 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3970 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3971 ctx = scmutil.revsingle(repo, rev, None)
3971 ctx = scmutil.revsingle(repo, rev, None)
3972
3972
3973 if ctx.rev() is None:
3973 if ctx.rev() is None:
3974 ctx = repo[None]
3974 ctx = repo[None]
3975 parents = ctx.parents()
3975 parents = ctx.parents()
3976 taglist = []
3976 taglist = []
3977 for p in parents:
3977 for p in parents:
3978 taglist.extend(p.tags())
3978 taglist.extend(p.tags())
3979
3979
3980 dirty = b""
3980 dirty = b""
3981 if ctx.dirty(missing=True, merge=False, branch=False):
3981 if ctx.dirty(missing=True, merge=False, branch=False):
3982 dirty = b'+'
3982 dirty = b'+'
3983 fm.data(dirty=dirty)
3983 fm.data(dirty=dirty)
3984
3984
3985 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3985 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3986 if default or id:
3986 if default or id:
3987 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3987 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3988 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3988 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3989
3989
3990 if num:
3990 if num:
3991 numoutput = [b"%d" % p.rev() for p in parents]
3991 numoutput = [b"%d" % p.rev() for p in parents]
3992 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3992 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3993
3993
3994 fm.data(
3994 fm.data(
3995 parents=fm.formatlist(
3995 parents=fm.formatlist(
3996 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3996 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3997 )
3997 )
3998 )
3998 )
3999 else:
3999 else:
4000 hexoutput = fm.hexfunc(ctx.node())
4000 hexoutput = fm.hexfunc(ctx.node())
4001 if default or id:
4001 if default or id:
4002 output = [hexoutput]
4002 output = [hexoutput]
4003 fm.data(id=hexoutput)
4003 fm.data(id=hexoutput)
4004
4004
4005 if num:
4005 if num:
4006 output.append(pycompat.bytestr(ctx.rev()))
4006 output.append(pycompat.bytestr(ctx.rev()))
4007 taglist = ctx.tags()
4007 taglist = ctx.tags()
4008
4008
4009 if default and not ui.quiet:
4009 if default and not ui.quiet:
4010 b = ctx.branch()
4010 b = ctx.branch()
4011 if b != b'default':
4011 if b != b'default':
4012 output.append(b"(%s)" % b)
4012 output.append(b"(%s)" % b)
4013
4013
4014 # multiple tags for a single parent separated by '/'
4014 # multiple tags for a single parent separated by '/'
4015 t = b'/'.join(taglist)
4015 t = b'/'.join(taglist)
4016 if t:
4016 if t:
4017 output.append(t)
4017 output.append(t)
4018
4018
4019 # multiple bookmarks for a single parent separated by '/'
4019 # multiple bookmarks for a single parent separated by '/'
4020 bm = b'/'.join(ctx.bookmarks())
4020 bm = b'/'.join(ctx.bookmarks())
4021 if bm:
4021 if bm:
4022 output.append(bm)
4022 output.append(bm)
4023 else:
4023 else:
4024 if branch:
4024 if branch:
4025 output.append(ctx.branch())
4025 output.append(ctx.branch())
4026
4026
4027 if tags:
4027 if tags:
4028 output.extend(taglist)
4028 output.extend(taglist)
4029
4029
4030 if bookmarks:
4030 if bookmarks:
4031 output.extend(ctx.bookmarks())
4031 output.extend(ctx.bookmarks())
4032
4032
4033 fm.data(node=ctx.hex())
4033 fm.data(node=ctx.hex())
4034 fm.data(branch=ctx.branch())
4034 fm.data(branch=ctx.branch())
4035 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4035 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4036 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4036 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4037 fm.context(ctx=ctx)
4037 fm.context(ctx=ctx)
4038
4038
4039 fm.plain(b"%s\n" % b' '.join(output))
4039 fm.plain(b"%s\n" % b' '.join(output))
4040 fm.end()
4040 fm.end()
4041
4041
4042
4042
4043 @command(
4043 @command(
4044 b'import|patch',
4044 b'import|patch',
4045 [
4045 [
4046 (
4046 (
4047 b'p',
4047 b'p',
4048 b'strip',
4048 b'strip',
4049 1,
4049 1,
4050 _(
4050 _(
4051 b'directory strip option for patch. This has the same '
4051 b'directory strip option for patch. This has the same '
4052 b'meaning as the corresponding patch option'
4052 b'meaning as the corresponding patch option'
4053 ),
4053 ),
4054 _(b'NUM'),
4054 _(b'NUM'),
4055 ),
4055 ),
4056 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4056 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4057 (b'', b'secret', None, _(b'use the secret phase for committing')),
4057 (b'', b'secret', None, _(b'use the secret phase for committing')),
4058 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4058 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4059 (
4059 (
4060 b'f',
4060 b'f',
4061 b'force',
4061 b'force',
4062 None,
4062 None,
4063 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4063 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4064 ),
4064 ),
4065 (
4065 (
4066 b'',
4066 b'',
4067 b'no-commit',
4067 b'no-commit',
4068 None,
4068 None,
4069 _(b"don't commit, just update the working directory"),
4069 _(b"don't commit, just update the working directory"),
4070 ),
4070 ),
4071 (
4071 (
4072 b'',
4072 b'',
4073 b'bypass',
4073 b'bypass',
4074 None,
4074 None,
4075 _(b"apply patch without touching the working directory"),
4075 _(b"apply patch without touching the working directory"),
4076 ),
4076 ),
4077 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4077 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4078 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4078 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4079 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4079 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4080 (
4080 (
4081 b'',
4081 b'',
4082 b'import-branch',
4082 b'import-branch',
4083 None,
4083 None,
4084 _(b'use any branch information in patch (implied by --exact)'),
4084 _(b'use any branch information in patch (implied by --exact)'),
4085 ),
4085 ),
4086 ]
4086 ]
4087 + commitopts
4087 + commitopts
4088 + commitopts2
4088 + commitopts2
4089 + similarityopts,
4089 + similarityopts,
4090 _(b'[OPTION]... PATCH...'),
4090 _(b'[OPTION]... PATCH...'),
4091 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4091 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4092 )
4092 )
4093 def import_(ui, repo, patch1=None, *patches, **opts):
4093 def import_(ui, repo, patch1=None, *patches, **opts):
4094 """import an ordered set of patches
4094 """import an ordered set of patches
4095
4095
4096 Import a list of patches and commit them individually (unless
4096 Import a list of patches and commit them individually (unless
4097 --no-commit is specified).
4097 --no-commit is specified).
4098
4098
4099 To read a patch from standard input (stdin), use "-" as the patch
4099 To read a patch from standard input (stdin), use "-" as the patch
4100 name. If a URL is specified, the patch will be downloaded from
4100 name. If a URL is specified, the patch will be downloaded from
4101 there.
4101 there.
4102
4102
4103 Import first applies changes to the working directory (unless
4103 Import first applies changes to the working directory (unless
4104 --bypass is specified), import will abort if there are outstanding
4104 --bypass is specified), import will abort if there are outstanding
4105 changes.
4105 changes.
4106
4106
4107 Use --bypass to apply and commit patches directly to the
4107 Use --bypass to apply and commit patches directly to the
4108 repository, without affecting the working directory. Without
4108 repository, without affecting the working directory. Without
4109 --exact, patches will be applied on top of the working directory
4109 --exact, patches will be applied on top of the working directory
4110 parent revision.
4110 parent revision.
4111
4111
4112 You can import a patch straight from a mail message. Even patches
4112 You can import a patch straight from a mail message. Even patches
4113 as attachments work (to use the body part, it must have type
4113 as attachments work (to use the body part, it must have type
4114 text/plain or text/x-patch). From and Subject headers of email
4114 text/plain or text/x-patch). From and Subject headers of email
4115 message are used as default committer and commit message. All
4115 message are used as default committer and commit message. All
4116 text/plain body parts before first diff are added to the commit
4116 text/plain body parts before first diff are added to the commit
4117 message.
4117 message.
4118
4118
4119 If the imported patch was generated by :hg:`export`, user and
4119 If the imported patch was generated by :hg:`export`, user and
4120 description from patch override values from message headers and
4120 description from patch override values from message headers and
4121 body. Values given on command line with -m/--message and -u/--user
4121 body. Values given on command line with -m/--message and -u/--user
4122 override these.
4122 override these.
4123
4123
4124 If --exact is specified, import will set the working directory to
4124 If --exact is specified, import will set the working directory to
4125 the parent of each patch before applying it, and will abort if the
4125 the parent of each patch before applying it, and will abort if the
4126 resulting changeset has a different ID than the one recorded in
4126 resulting changeset has a different ID than the one recorded in
4127 the patch. This will guard against various ways that portable
4127 the patch. This will guard against various ways that portable
4128 patch formats and mail systems might fail to transfer Mercurial
4128 patch formats and mail systems might fail to transfer Mercurial
4129 data or metadata. See :hg:`bundle` for lossless transmission.
4129 data or metadata. See :hg:`bundle` for lossless transmission.
4130
4130
4131 Use --partial to ensure a changeset will be created from the patch
4131 Use --partial to ensure a changeset will be created from the patch
4132 even if some hunks fail to apply. Hunks that fail to apply will be
4132 even if some hunks fail to apply. Hunks that fail to apply will be
4133 written to a <target-file>.rej file. Conflicts can then be resolved
4133 written to a <target-file>.rej file. Conflicts can then be resolved
4134 by hand before :hg:`commit --amend` is run to update the created
4134 by hand before :hg:`commit --amend` is run to update the created
4135 changeset. This flag exists to let people import patches that
4135 changeset. This flag exists to let people import patches that
4136 partially apply without losing the associated metadata (author,
4136 partially apply without losing the associated metadata (author,
4137 date, description, ...).
4137 date, description, ...).
4138
4138
4139 .. note::
4139 .. note::
4140
4140
4141 When no hunks apply cleanly, :hg:`import --partial` will create
4141 When no hunks apply cleanly, :hg:`import --partial` will create
4142 an empty changeset, importing only the patch metadata.
4142 an empty changeset, importing only the patch metadata.
4143
4143
4144 With -s/--similarity, hg will attempt to discover renames and
4144 With -s/--similarity, hg will attempt to discover renames and
4145 copies in the patch in the same way as :hg:`addremove`.
4145 copies in the patch in the same way as :hg:`addremove`.
4146
4146
4147 It is possible to use external patch programs to perform the patch
4147 It is possible to use external patch programs to perform the patch
4148 by setting the ``ui.patch`` configuration option. For the default
4148 by setting the ``ui.patch`` configuration option. For the default
4149 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4149 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4150 See :hg:`help config` for more information about configuration
4150 See :hg:`help config` for more information about configuration
4151 files and how to use these options.
4151 files and how to use these options.
4152
4152
4153 See :hg:`help dates` for a list of formats valid for -d/--date.
4153 See :hg:`help dates` for a list of formats valid for -d/--date.
4154
4154
4155 .. container:: verbose
4155 .. container:: verbose
4156
4156
4157 Examples:
4157 Examples:
4158
4158
4159 - import a traditional patch from a website and detect renames::
4159 - import a traditional patch from a website and detect renames::
4160
4160
4161 hg import -s 80 http://example.com/bugfix.patch
4161 hg import -s 80 http://example.com/bugfix.patch
4162
4162
4163 - import a changeset from an hgweb server::
4163 - import a changeset from an hgweb server::
4164
4164
4165 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4165 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4166
4166
4167 - import all the patches in an Unix-style mbox::
4167 - import all the patches in an Unix-style mbox::
4168
4168
4169 hg import incoming-patches.mbox
4169 hg import incoming-patches.mbox
4170
4170
4171 - import patches from stdin::
4171 - import patches from stdin::
4172
4172
4173 hg import -
4173 hg import -
4174
4174
4175 - attempt to exactly restore an exported changeset (not always
4175 - attempt to exactly restore an exported changeset (not always
4176 possible)::
4176 possible)::
4177
4177
4178 hg import --exact proposed-fix.patch
4178 hg import --exact proposed-fix.patch
4179
4179
4180 - use an external tool to apply a patch which is too fuzzy for
4180 - use an external tool to apply a patch which is too fuzzy for
4181 the default internal tool.
4181 the default internal tool.
4182
4182
4183 hg import --config ui.patch="patch --merge" fuzzy.patch
4183 hg import --config ui.patch="patch --merge" fuzzy.patch
4184
4184
4185 - change the default fuzzing from 2 to a less strict 7
4185 - change the default fuzzing from 2 to a less strict 7
4186
4186
4187 hg import --config ui.fuzz=7 fuzz.patch
4187 hg import --config ui.fuzz=7 fuzz.patch
4188
4188
4189 Returns 0 on success, 1 on partial success (see --partial).
4189 Returns 0 on success, 1 on partial success (see --partial).
4190 """
4190 """
4191
4191
4192 opts = pycompat.byteskwargs(opts)
4192 opts = pycompat.byteskwargs(opts)
4193 if not patch1:
4193 if not patch1:
4194 raise error.Abort(_(b'need at least one patch to import'))
4194 raise error.Abort(_(b'need at least one patch to import'))
4195
4195
4196 patches = (patch1,) + patches
4196 patches = (patch1,) + patches
4197
4197
4198 date = opts.get(b'date')
4198 date = opts.get(b'date')
4199 if date:
4199 if date:
4200 opts[b'date'] = dateutil.parsedate(date)
4200 opts[b'date'] = dateutil.parsedate(date)
4201
4201
4202 exact = opts.get(b'exact')
4202 exact = opts.get(b'exact')
4203 update = not opts.get(b'bypass')
4203 update = not opts.get(b'bypass')
4204 if not update and opts.get(b'no_commit'):
4204 if not update and opts.get(b'no_commit'):
4205 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4205 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4206 if opts.get(b'secret') and opts.get(b'no_commit'):
4206 if opts.get(b'secret') and opts.get(b'no_commit'):
4207 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4207 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4208 try:
4208 try:
4209 sim = float(opts.get(b'similarity') or 0)
4209 sim = float(opts.get(b'similarity') or 0)
4210 except ValueError:
4210 except ValueError:
4211 raise error.Abort(_(b'similarity must be a number'))
4211 raise error.Abort(_(b'similarity must be a number'))
4212 if sim < 0 or sim > 100:
4212 if sim < 0 or sim > 100:
4213 raise error.Abort(_(b'similarity must be between 0 and 100'))
4213 raise error.Abort(_(b'similarity must be between 0 and 100'))
4214 if sim and not update:
4214 if sim and not update:
4215 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4215 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4216 if exact:
4216 if exact:
4217 if opts.get(b'edit'):
4217 if opts.get(b'edit'):
4218 raise error.Abort(_(b'cannot use --exact with --edit'))
4218 raise error.Abort(_(b'cannot use --exact with --edit'))
4219 if opts.get(b'prefix'):
4219 if opts.get(b'prefix'):
4220 raise error.Abort(_(b'cannot use --exact with --prefix'))
4220 raise error.Abort(_(b'cannot use --exact with --prefix'))
4221
4221
4222 base = opts[b"base"]
4222 base = opts[b"base"]
4223 msgs = []
4223 msgs = []
4224 ret = 0
4224 ret = 0
4225
4225
4226 with repo.wlock():
4226 with repo.wlock():
4227 if update:
4227 if update:
4228 cmdutil.checkunfinished(repo)
4228 cmdutil.checkunfinished(repo)
4229 if exact or not opts.get(b'force'):
4229 if exact or not opts.get(b'force'):
4230 cmdutil.bailifchanged(repo)
4230 cmdutil.bailifchanged(repo)
4231
4231
4232 if not opts.get(b'no_commit'):
4232 if not opts.get(b'no_commit'):
4233 lock = repo.lock
4233 lock = repo.lock
4234 tr = lambda: repo.transaction(b'import')
4234 tr = lambda: repo.transaction(b'import')
4235 dsguard = util.nullcontextmanager
4235 dsguard = util.nullcontextmanager
4236 else:
4236 else:
4237 lock = util.nullcontextmanager
4237 lock = util.nullcontextmanager
4238 tr = util.nullcontextmanager
4238 tr = util.nullcontextmanager
4239 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4239 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4240 with lock(), tr(), dsguard():
4240 with lock(), tr(), dsguard():
4241 parents = repo[None].parents()
4241 parents = repo[None].parents()
4242 for patchurl in patches:
4242 for patchurl in patches:
4243 if patchurl == b'-':
4243 if patchurl == b'-':
4244 ui.status(_(b'applying patch from stdin\n'))
4244 ui.status(_(b'applying patch from stdin\n'))
4245 patchfile = ui.fin
4245 patchfile = ui.fin
4246 patchurl = b'stdin' # for error message
4246 patchurl = b'stdin' # for error message
4247 else:
4247 else:
4248 patchurl = os.path.join(base, patchurl)
4248 patchurl = os.path.join(base, patchurl)
4249 ui.status(_(b'applying %s\n') % patchurl)
4249 ui.status(_(b'applying %s\n') % patchurl)
4250 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4250 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4251
4251
4252 haspatch = False
4252 haspatch = False
4253 for hunk in patch.split(patchfile):
4253 for hunk in patch.split(patchfile):
4254 with patch.extract(ui, hunk) as patchdata:
4254 with patch.extract(ui, hunk) as patchdata:
4255 msg, node, rej = cmdutil.tryimportone(
4255 msg, node, rej = cmdutil.tryimportone(
4256 ui, repo, patchdata, parents, opts, msgs, hg.clean
4256 ui, repo, patchdata, parents, opts, msgs, hg.clean
4257 )
4257 )
4258 if msg:
4258 if msg:
4259 haspatch = True
4259 haspatch = True
4260 ui.note(msg + b'\n')
4260 ui.note(msg + b'\n')
4261 if update or exact:
4261 if update or exact:
4262 parents = repo[None].parents()
4262 parents = repo[None].parents()
4263 else:
4263 else:
4264 parents = [repo[node]]
4264 parents = [repo[node]]
4265 if rej:
4265 if rej:
4266 ui.write_err(_(b"patch applied partially\n"))
4266 ui.write_err(_(b"patch applied partially\n"))
4267 ui.write_err(
4267 ui.write_err(
4268 _(
4268 _(
4269 b"(fix the .rej files and run "
4269 b"(fix the .rej files and run "
4270 b"`hg commit --amend`)\n"
4270 b"`hg commit --amend`)\n"
4271 )
4271 )
4272 )
4272 )
4273 ret = 1
4273 ret = 1
4274 break
4274 break
4275
4275
4276 if not haspatch:
4276 if not haspatch:
4277 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4277 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4278
4278
4279 if msgs:
4279 if msgs:
4280 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4280 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4281 return ret
4281 return ret
4282
4282
4283
4283
4284 @command(
4284 @command(
4285 b'incoming|in',
4285 b'incoming|in',
4286 [
4286 [
4287 (
4287 (
4288 b'f',
4288 b'f',
4289 b'force',
4289 b'force',
4290 None,
4290 None,
4291 _(b'run even if remote repository is unrelated'),
4291 _(b'run even if remote repository is unrelated'),
4292 ),
4292 ),
4293 (b'n', b'newest-first', None, _(b'show newest record first')),
4293 (b'n', b'newest-first', None, _(b'show newest record first')),
4294 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4294 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4295 (
4295 (
4296 b'r',
4296 b'r',
4297 b'rev',
4297 b'rev',
4298 [],
4298 [],
4299 _(b'a remote changeset intended to be added'),
4299 _(b'a remote changeset intended to be added'),
4300 _(b'REV'),
4300 _(b'REV'),
4301 ),
4301 ),
4302 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4302 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4303 (
4303 (
4304 b'b',
4304 b'b',
4305 b'branch',
4305 b'branch',
4306 [],
4306 [],
4307 _(b'a specific branch you would like to pull'),
4307 _(b'a specific branch you would like to pull'),
4308 _(b'BRANCH'),
4308 _(b'BRANCH'),
4309 ),
4309 ),
4310 ]
4310 ]
4311 + logopts
4311 + logopts
4312 + remoteopts
4312 + remoteopts
4313 + subrepoopts,
4313 + subrepoopts,
4314 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4314 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4315 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4315 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4316 )
4316 )
4317 def incoming(ui, repo, source=b"default", **opts):
4317 def incoming(ui, repo, source=b"default", **opts):
4318 """show new changesets found in source
4318 """show new changesets found in source
4319
4319
4320 Show new changesets found in the specified path/URL or the default
4320 Show new changesets found in the specified path/URL or the default
4321 pull location. These are the changesets that would have been pulled
4321 pull location. These are the changesets that would have been pulled
4322 by :hg:`pull` at the time you issued this command.
4322 by :hg:`pull` at the time you issued this command.
4323
4323
4324 See pull for valid source format details.
4324 See pull for valid source format details.
4325
4325
4326 .. container:: verbose
4326 .. container:: verbose
4327
4327
4328 With -B/--bookmarks, the result of bookmark comparison between
4328 With -B/--bookmarks, the result of bookmark comparison between
4329 local and remote repositories is displayed. With -v/--verbose,
4329 local and remote repositories is displayed. With -v/--verbose,
4330 status is also displayed for each bookmark like below::
4330 status is also displayed for each bookmark like below::
4331
4331
4332 BM1 01234567890a added
4332 BM1 01234567890a added
4333 BM2 1234567890ab advanced
4333 BM2 1234567890ab advanced
4334 BM3 234567890abc diverged
4334 BM3 234567890abc diverged
4335 BM4 34567890abcd changed
4335 BM4 34567890abcd changed
4336
4336
4337 The action taken locally when pulling depends on the
4337 The action taken locally when pulling depends on the
4338 status of each bookmark:
4338 status of each bookmark:
4339
4339
4340 :``added``: pull will create it
4340 :``added``: pull will create it
4341 :``advanced``: pull will update it
4341 :``advanced``: pull will update it
4342 :``diverged``: pull will create a divergent bookmark
4342 :``diverged``: pull will create a divergent bookmark
4343 :``changed``: result depends on remote changesets
4343 :``changed``: result depends on remote changesets
4344
4344
4345 From the point of view of pulling behavior, bookmark
4345 From the point of view of pulling behavior, bookmark
4346 existing only in the remote repository are treated as ``added``,
4346 existing only in the remote repository are treated as ``added``,
4347 even if it is in fact locally deleted.
4347 even if it is in fact locally deleted.
4348
4348
4349 .. container:: verbose
4349 .. container:: verbose
4350
4350
4351 For remote repository, using --bundle avoids downloading the
4351 For remote repository, using --bundle avoids downloading the
4352 changesets twice if the incoming is followed by a pull.
4352 changesets twice if the incoming is followed by a pull.
4353
4353
4354 Examples:
4354 Examples:
4355
4355
4356 - show incoming changes with patches and full description::
4356 - show incoming changes with patches and full description::
4357
4357
4358 hg incoming -vp
4358 hg incoming -vp
4359
4359
4360 - show incoming changes excluding merges, store a bundle::
4360 - show incoming changes excluding merges, store a bundle::
4361
4361
4362 hg in -vpM --bundle incoming.hg
4362 hg in -vpM --bundle incoming.hg
4363 hg pull incoming.hg
4363 hg pull incoming.hg
4364
4364
4365 - briefly list changes inside a bundle::
4365 - briefly list changes inside a bundle::
4366
4366
4367 hg in changes.hg -T "{desc|firstline}\\n"
4367 hg in changes.hg -T "{desc|firstline}\\n"
4368
4368
4369 Returns 0 if there are incoming changes, 1 otherwise.
4369 Returns 0 if there are incoming changes, 1 otherwise.
4370 """
4370 """
4371 opts = pycompat.byteskwargs(opts)
4371 opts = pycompat.byteskwargs(opts)
4372 if opts.get(b'graph'):
4372 if opts.get(b'graph'):
4373 logcmdutil.checkunsupportedgraphflags([], opts)
4373 logcmdutil.checkunsupportedgraphflags([], opts)
4374
4374
4375 def display(other, chlist, displayer):
4375 def display(other, chlist, displayer):
4376 revdag = logcmdutil.graphrevs(other, chlist, opts)
4376 revdag = logcmdutil.graphrevs(other, chlist, opts)
4377 logcmdutil.displaygraph(
4377 logcmdutil.displaygraph(
4378 ui, repo, revdag, displayer, graphmod.asciiedges
4378 ui, repo, revdag, displayer, graphmod.asciiedges
4379 )
4379 )
4380
4380
4381 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4381 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4382 return 0
4382 return 0
4383
4383
4384 if opts.get(b'bundle') and opts.get(b'subrepos'):
4384 if opts.get(b'bundle') and opts.get(b'subrepos'):
4385 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4385 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4386
4386
4387 if opts.get(b'bookmarks'):
4387 if opts.get(b'bookmarks'):
4388 source, branches = hg.parseurl(
4388 source, branches = hg.parseurl(
4389 ui.expandpath(source), opts.get(b'branch')
4389 ui.expandpath(source), opts.get(b'branch')
4390 )
4390 )
4391 other = hg.peer(repo, opts, source)
4391 other = hg.peer(repo, opts, source)
4392 if b'bookmarks' not in other.listkeys(b'namespaces'):
4392 if b'bookmarks' not in other.listkeys(b'namespaces'):
4393 ui.warn(_(b"remote doesn't support bookmarks\n"))
4393 ui.warn(_(b"remote doesn't support bookmarks\n"))
4394 return 0
4394 return 0
4395 ui.pager(b'incoming')
4395 ui.pager(b'incoming')
4396 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4396 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4397 return bookmarks.incoming(ui, repo, other)
4397 return bookmarks.incoming(ui, repo, other)
4398
4398
4399 repo._subtoppath = ui.expandpath(source)
4399 repo._subtoppath = ui.expandpath(source)
4400 try:
4400 try:
4401 return hg.incoming(ui, repo, source, opts)
4401 return hg.incoming(ui, repo, source, opts)
4402 finally:
4402 finally:
4403 del repo._subtoppath
4403 del repo._subtoppath
4404
4404
4405
4405
4406 @command(
4406 @command(
4407 b'init',
4407 b'init',
4408 remoteopts,
4408 remoteopts,
4409 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4409 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4410 helpcategory=command.CATEGORY_REPO_CREATION,
4410 helpcategory=command.CATEGORY_REPO_CREATION,
4411 helpbasic=True,
4411 helpbasic=True,
4412 norepo=True,
4412 norepo=True,
4413 )
4413 )
4414 def init(ui, dest=b".", **opts):
4414 def init(ui, dest=b".", **opts):
4415 """create a new repository in the given directory
4415 """create a new repository in the given directory
4416
4416
4417 Initialize a new repository in the given directory. If the given
4417 Initialize a new repository in the given directory. If the given
4418 directory does not exist, it will be created.
4418 directory does not exist, it will be created.
4419
4419
4420 If no directory is given, the current directory is used.
4420 If no directory is given, the current directory is used.
4421
4421
4422 It is possible to specify an ``ssh://`` URL as the destination.
4422 It is possible to specify an ``ssh://`` URL as the destination.
4423 See :hg:`help urls` for more information.
4423 See :hg:`help urls` for more information.
4424
4424
4425 Returns 0 on success.
4425 Returns 0 on success.
4426 """
4426 """
4427 opts = pycompat.byteskwargs(opts)
4427 opts = pycompat.byteskwargs(opts)
4428 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4428 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4429
4429
4430
4430
4431 @command(
4431 @command(
4432 b'locate',
4432 b'locate',
4433 [
4433 [
4434 (
4434 (
4435 b'r',
4435 b'r',
4436 b'rev',
4436 b'rev',
4437 b'',
4437 b'',
4438 _(b'search the repository as it is in REV'),
4438 _(b'search the repository as it is in REV'),
4439 _(b'REV'),
4439 _(b'REV'),
4440 ),
4440 ),
4441 (
4441 (
4442 b'0',
4442 b'0',
4443 b'print0',
4443 b'print0',
4444 None,
4444 None,
4445 _(b'end filenames with NUL, for use with xargs'),
4445 _(b'end filenames with NUL, for use with xargs'),
4446 ),
4446 ),
4447 (
4447 (
4448 b'f',
4448 b'f',
4449 b'fullpath',
4449 b'fullpath',
4450 None,
4450 None,
4451 _(b'print complete paths from the filesystem root'),
4451 _(b'print complete paths from the filesystem root'),
4452 ),
4452 ),
4453 ]
4453 ]
4454 + walkopts,
4454 + walkopts,
4455 _(b'[OPTION]... [PATTERN]...'),
4455 _(b'[OPTION]... [PATTERN]...'),
4456 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4456 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4457 )
4457 )
4458 def locate(ui, repo, *pats, **opts):
4458 def locate(ui, repo, *pats, **opts):
4459 """locate files matching specific patterns (DEPRECATED)
4459 """locate files matching specific patterns (DEPRECATED)
4460
4460
4461 Print files under Mercurial control in the working directory whose
4461 Print files under Mercurial control in the working directory whose
4462 names match the given patterns.
4462 names match the given patterns.
4463
4463
4464 By default, this command searches all directories in the working
4464 By default, this command searches all directories in the working
4465 directory. To search just the current directory and its
4465 directory. To search just the current directory and its
4466 subdirectories, use "--include .".
4466 subdirectories, use "--include .".
4467
4467
4468 If no patterns are given to match, this command prints the names
4468 If no patterns are given to match, this command prints the names
4469 of all files under Mercurial control in the working directory.
4469 of all files under Mercurial control in the working directory.
4470
4470
4471 If you want to feed the output of this command into the "xargs"
4471 If you want to feed the output of this command into the "xargs"
4472 command, use the -0 option to both this command and "xargs". This
4472 command, use the -0 option to both this command and "xargs". This
4473 will avoid the problem of "xargs" treating single filenames that
4473 will avoid the problem of "xargs" treating single filenames that
4474 contain whitespace as multiple filenames.
4474 contain whitespace as multiple filenames.
4475
4475
4476 See :hg:`help files` for a more versatile command.
4476 See :hg:`help files` for a more versatile command.
4477
4477
4478 Returns 0 if a match is found, 1 otherwise.
4478 Returns 0 if a match is found, 1 otherwise.
4479 """
4479 """
4480 opts = pycompat.byteskwargs(opts)
4480 opts = pycompat.byteskwargs(opts)
4481 if opts.get(b'print0'):
4481 if opts.get(b'print0'):
4482 end = b'\0'
4482 end = b'\0'
4483 else:
4483 else:
4484 end = b'\n'
4484 end = b'\n'
4485 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4485 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4486
4486
4487 ret = 1
4487 ret = 1
4488 m = scmutil.match(
4488 m = scmutil.match(
4489 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4489 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4490 )
4490 )
4491
4491
4492 ui.pager(b'locate')
4492 ui.pager(b'locate')
4493 if ctx.rev() is None:
4493 if ctx.rev() is None:
4494 # When run on the working copy, "locate" includes removed files, so
4494 # When run on the working copy, "locate" includes removed files, so
4495 # we get the list of files from the dirstate.
4495 # we get the list of files from the dirstate.
4496 filesgen = sorted(repo.dirstate.matches(m))
4496 filesgen = sorted(repo.dirstate.matches(m))
4497 else:
4497 else:
4498 filesgen = ctx.matches(m)
4498 filesgen = ctx.matches(m)
4499 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4499 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4500 for abs in filesgen:
4500 for abs in filesgen:
4501 if opts.get(b'fullpath'):
4501 if opts.get(b'fullpath'):
4502 ui.write(repo.wjoin(abs), end)
4502 ui.write(repo.wjoin(abs), end)
4503 else:
4503 else:
4504 ui.write(uipathfn(abs), end)
4504 ui.write(uipathfn(abs), end)
4505 ret = 0
4505 ret = 0
4506
4506
4507 return ret
4507 return ret
4508
4508
4509
4509
4510 @command(
4510 @command(
4511 b'log|history',
4511 b'log|history',
4512 [
4512 [
4513 (
4513 (
4514 b'f',
4514 b'f',
4515 b'follow',
4515 b'follow',
4516 None,
4516 None,
4517 _(
4517 _(
4518 b'follow changeset history, or file history across copies and renames'
4518 b'follow changeset history, or file history across copies and renames'
4519 ),
4519 ),
4520 ),
4520 ),
4521 (
4521 (
4522 b'',
4522 b'',
4523 b'follow-first',
4523 b'follow-first',
4524 None,
4524 None,
4525 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4525 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4526 ),
4526 ),
4527 (
4527 (
4528 b'd',
4528 b'd',
4529 b'date',
4529 b'date',
4530 b'',
4530 b'',
4531 _(b'show revisions matching date spec'),
4531 _(b'show revisions matching date spec'),
4532 _(b'DATE'),
4532 _(b'DATE'),
4533 ),
4533 ),
4534 (b'C', b'copies', None, _(b'show copied files')),
4534 (b'C', b'copies', None, _(b'show copied files')),
4535 (
4535 (
4536 b'k',
4536 b'k',
4537 b'keyword',
4537 b'keyword',
4538 [],
4538 [],
4539 _(b'do case-insensitive search for a given text'),
4539 _(b'do case-insensitive search for a given text'),
4540 _(b'TEXT'),
4540 _(b'TEXT'),
4541 ),
4541 ),
4542 (
4542 (
4543 b'r',
4543 b'r',
4544 b'rev',
4544 b'rev',
4545 [],
4545 [],
4546 _(b'show the specified revision or revset'),
4546 _(b'show the specified revision or revset'),
4547 _(b'REV'),
4547 _(b'REV'),
4548 ),
4548 ),
4549 (
4549 (
4550 b'L',
4550 b'L',
4551 b'line-range',
4551 b'line-range',
4552 [],
4552 [],
4553 _(b'follow line range of specified file (EXPERIMENTAL)'),
4553 _(b'follow line range of specified file (EXPERIMENTAL)'),
4554 _(b'FILE,RANGE'),
4554 _(b'FILE,RANGE'),
4555 ),
4555 ),
4556 (
4556 (
4557 b'',
4557 b'',
4558 b'removed',
4558 b'removed',
4559 None,
4559 None,
4560 _(b'include revisions where files were removed'),
4560 _(b'include revisions where files were removed'),
4561 ),
4561 ),
4562 (
4562 (
4563 b'm',
4563 b'm',
4564 b'only-merges',
4564 b'only-merges',
4565 None,
4565 None,
4566 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4566 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4567 ),
4567 ),
4568 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4568 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4569 (
4569 (
4570 b'',
4570 b'',
4571 b'only-branch',
4571 b'only-branch',
4572 [],
4572 [],
4573 _(
4573 _(
4574 b'show only changesets within the given named branch (DEPRECATED)'
4574 b'show only changesets within the given named branch (DEPRECATED)'
4575 ),
4575 ),
4576 _(b'BRANCH'),
4576 _(b'BRANCH'),
4577 ),
4577 ),
4578 (
4578 (
4579 b'b',
4579 b'b',
4580 b'branch',
4580 b'branch',
4581 [],
4581 [],
4582 _(b'show changesets within the given named branch'),
4582 _(b'show changesets within the given named branch'),
4583 _(b'BRANCH'),
4583 _(b'BRANCH'),
4584 ),
4584 ),
4585 (
4585 (
4586 b'P',
4586 b'P',
4587 b'prune',
4587 b'prune',
4588 [],
4588 [],
4589 _(b'do not display revision or any of its ancestors'),
4589 _(b'do not display revision or any of its ancestors'),
4590 _(b'REV'),
4590 _(b'REV'),
4591 ),
4591 ),
4592 ]
4592 ]
4593 + logopts
4593 + logopts
4594 + walkopts,
4594 + walkopts,
4595 _(b'[OPTION]... [FILE]'),
4595 _(b'[OPTION]... [FILE]'),
4596 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4596 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4597 helpbasic=True,
4597 helpbasic=True,
4598 inferrepo=True,
4598 inferrepo=True,
4599 intents={INTENT_READONLY},
4599 intents={INTENT_READONLY},
4600 )
4600 )
4601 def log(ui, repo, *pats, **opts):
4601 def log(ui, repo, *pats, **opts):
4602 """show revision history of entire repository or files
4602 """show revision history of entire repository or files
4603
4603
4604 Print the revision history of the specified files or the entire
4604 Print the revision history of the specified files or the entire
4605 project.
4605 project.
4606
4606
4607 If no revision range is specified, the default is ``tip:0`` unless
4607 If no revision range is specified, the default is ``tip:0`` unless
4608 --follow is set, in which case the working directory parent is
4608 --follow is set, in which case the working directory parent is
4609 used as the starting revision.
4609 used as the starting revision.
4610
4610
4611 File history is shown without following rename or copy history of
4611 File history is shown without following rename or copy history of
4612 files. Use -f/--follow with a filename to follow history across
4612 files. Use -f/--follow with a filename to follow history across
4613 renames and copies. --follow without a filename will only show
4613 renames and copies. --follow without a filename will only show
4614 ancestors of the starting revision.
4614 ancestors of the starting revision.
4615
4615
4616 By default this command prints revision number and changeset id,
4616 By default this command prints revision number and changeset id,
4617 tags, non-trivial parents, user, date and time, and a summary for
4617 tags, non-trivial parents, user, date and time, and a summary for
4618 each commit. When the -v/--verbose switch is used, the list of
4618 each commit. When the -v/--verbose switch is used, the list of
4619 changed files and full commit message are shown.
4619 changed files and full commit message are shown.
4620
4620
4621 With --graph the revisions are shown as an ASCII art DAG with the most
4621 With --graph the revisions are shown as an ASCII art DAG with the most
4622 recent changeset at the top.
4622 recent changeset at the top.
4623 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4623 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4624 involved in an unresolved merge conflict, '_' closes a branch,
4624 involved in an unresolved merge conflict, '_' closes a branch,
4625 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4625 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4626 changeset from the lines below is a parent of the 'o' merge on the same
4626 changeset from the lines below is a parent of the 'o' merge on the same
4627 line.
4627 line.
4628 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4628 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4629 of a '|' indicates one or more revisions in a path are omitted.
4629 of a '|' indicates one or more revisions in a path are omitted.
4630
4630
4631 .. container:: verbose
4631 .. container:: verbose
4632
4632
4633 Use -L/--line-range FILE,M:N options to follow the history of lines
4633 Use -L/--line-range FILE,M:N options to follow the history of lines
4634 from M to N in FILE. With -p/--patch only diff hunks affecting
4634 from M to N in FILE. With -p/--patch only diff hunks affecting
4635 specified line range will be shown. This option requires --follow;
4635 specified line range will be shown. This option requires --follow;
4636 it can be specified multiple times. Currently, this option is not
4636 it can be specified multiple times. Currently, this option is not
4637 compatible with --graph. This option is experimental.
4637 compatible with --graph. This option is experimental.
4638
4638
4639 .. note::
4639 .. note::
4640
4640
4641 :hg:`log --patch` may generate unexpected diff output for merge
4641 :hg:`log --patch` may generate unexpected diff output for merge
4642 changesets, as it will only compare the merge changeset against
4642 changesets, as it will only compare the merge changeset against
4643 its first parent. Also, only files different from BOTH parents
4643 its first parent. Also, only files different from BOTH parents
4644 will appear in files:.
4644 will appear in files:.
4645
4645
4646 .. note::
4646 .. note::
4647
4647
4648 For performance reasons, :hg:`log FILE` may omit duplicate changes
4648 For performance reasons, :hg:`log FILE` may omit duplicate changes
4649 made on branches and will not show removals or mode changes. To
4649 made on branches and will not show removals or mode changes. To
4650 see all such changes, use the --removed switch.
4650 see all such changes, use the --removed switch.
4651
4651
4652 .. container:: verbose
4652 .. container:: verbose
4653
4653
4654 .. note::
4654 .. note::
4655
4655
4656 The history resulting from -L/--line-range options depends on diff
4656 The history resulting from -L/--line-range options depends on diff
4657 options; for instance if white-spaces are ignored, respective changes
4657 options; for instance if white-spaces are ignored, respective changes
4658 with only white-spaces in specified line range will not be listed.
4658 with only white-spaces in specified line range will not be listed.
4659
4659
4660 .. container:: verbose
4660 .. container:: verbose
4661
4661
4662 Some examples:
4662 Some examples:
4663
4663
4664 - changesets with full descriptions and file lists::
4664 - changesets with full descriptions and file lists::
4665
4665
4666 hg log -v
4666 hg log -v
4667
4667
4668 - changesets ancestral to the working directory::
4668 - changesets ancestral to the working directory::
4669
4669
4670 hg log -f
4670 hg log -f
4671
4671
4672 - last 10 commits on the current branch::
4672 - last 10 commits on the current branch::
4673
4673
4674 hg log -l 10 -b .
4674 hg log -l 10 -b .
4675
4675
4676 - changesets showing all modifications of a file, including removals::
4676 - changesets showing all modifications of a file, including removals::
4677
4677
4678 hg log --removed file.c
4678 hg log --removed file.c
4679
4679
4680 - all changesets that touch a directory, with diffs, excluding merges::
4680 - all changesets that touch a directory, with diffs, excluding merges::
4681
4681
4682 hg log -Mp lib/
4682 hg log -Mp lib/
4683
4683
4684 - all revision numbers that match a keyword::
4684 - all revision numbers that match a keyword::
4685
4685
4686 hg log -k bug --template "{rev}\\n"
4686 hg log -k bug --template "{rev}\\n"
4687
4687
4688 - the full hash identifier of the working directory parent::
4688 - the full hash identifier of the working directory parent::
4689
4689
4690 hg log -r . --template "{node}\\n"
4690 hg log -r . --template "{node}\\n"
4691
4691
4692 - list available log templates::
4692 - list available log templates::
4693
4693
4694 hg log -T list
4694 hg log -T list
4695
4695
4696 - check if a given changeset is included in a tagged release::
4696 - check if a given changeset is included in a tagged release::
4697
4697
4698 hg log -r "a21ccf and ancestor(1.9)"
4698 hg log -r "a21ccf and ancestor(1.9)"
4699
4699
4700 - find all changesets by some user in a date range::
4700 - find all changesets by some user in a date range::
4701
4701
4702 hg log -k alice -d "may 2008 to jul 2008"
4702 hg log -k alice -d "may 2008 to jul 2008"
4703
4703
4704 - summary of all changesets after the last tag::
4704 - summary of all changesets after the last tag::
4705
4705
4706 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4706 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4707
4707
4708 - changesets touching lines 13 to 23 for file.c::
4708 - changesets touching lines 13 to 23 for file.c::
4709
4709
4710 hg log -L file.c,13:23
4710 hg log -L file.c,13:23
4711
4711
4712 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4712 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4713 main.c with patch::
4713 main.c with patch::
4714
4714
4715 hg log -L file.c,13:23 -L main.c,2:6 -p
4715 hg log -L file.c,13:23 -L main.c,2:6 -p
4716
4716
4717 See :hg:`help dates` for a list of formats valid for -d/--date.
4717 See :hg:`help dates` for a list of formats valid for -d/--date.
4718
4718
4719 See :hg:`help revisions` for more about specifying and ordering
4719 See :hg:`help revisions` for more about specifying and ordering
4720 revisions.
4720 revisions.
4721
4721
4722 See :hg:`help templates` for more about pre-packaged styles and
4722 See :hg:`help templates` for more about pre-packaged styles and
4723 specifying custom templates. The default template used by the log
4723 specifying custom templates. The default template used by the log
4724 command can be customized via the ``ui.logtemplate`` configuration
4724 command can be customized via the ``ui.logtemplate`` configuration
4725 setting.
4725 setting.
4726
4726
4727 Returns 0 on success.
4727 Returns 0 on success.
4728
4728
4729 """
4729 """
4730 opts = pycompat.byteskwargs(opts)
4730 opts = pycompat.byteskwargs(opts)
4731 linerange = opts.get(b'line_range')
4731 linerange = opts.get(b'line_range')
4732
4732
4733 if linerange and not opts.get(b'follow'):
4733 if linerange and not opts.get(b'follow'):
4734 raise error.Abort(_(b'--line-range requires --follow'))
4734 raise error.Abort(_(b'--line-range requires --follow'))
4735
4735
4736 if linerange and pats:
4736 if linerange and pats:
4737 # TODO: take pats as patterns with no line-range filter
4737 # TODO: take pats as patterns with no line-range filter
4738 raise error.Abort(
4738 raise error.Abort(
4739 _(b'FILE arguments are not compatible with --line-range option')
4739 _(b'FILE arguments are not compatible with --line-range option')
4740 )
4740 )
4741
4741
4742 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4742 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4743 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4743 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4744 if linerange:
4744 if linerange:
4745 # TODO: should follow file history from logcmdutil._initialrevs(),
4745 # TODO: should follow file history from logcmdutil._initialrevs(),
4746 # then filter the result by logcmdutil._makerevset() and --limit
4746 # then filter the result by logcmdutil._makerevset() and --limit
4747 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4747 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4748
4748
4749 getcopies = None
4749 getcopies = None
4750 if opts.get(b'copies'):
4750 if opts.get(b'copies'):
4751 endrev = None
4751 endrev = None
4752 if revs:
4752 if revs:
4753 endrev = revs.max() + 1
4753 endrev = revs.max() + 1
4754 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4754 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4755
4755
4756 ui.pager(b'log')
4756 ui.pager(b'log')
4757 displayer = logcmdutil.changesetdisplayer(
4757 displayer = logcmdutil.changesetdisplayer(
4758 ui, repo, opts, differ, buffered=True
4758 ui, repo, opts, differ, buffered=True
4759 )
4759 )
4760 if opts.get(b'graph'):
4760 if opts.get(b'graph'):
4761 displayfn = logcmdutil.displaygraphrevs
4761 displayfn = logcmdutil.displaygraphrevs
4762 else:
4762 else:
4763 displayfn = logcmdutil.displayrevs
4763 displayfn = logcmdutil.displayrevs
4764 displayfn(ui, repo, revs, displayer, getcopies)
4764 displayfn(ui, repo, revs, displayer, getcopies)
4765
4765
4766
4766
4767 @command(
4767 @command(
4768 b'manifest',
4768 b'manifest',
4769 [
4769 [
4770 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4770 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4771 (b'', b'all', False, _(b"list files from all revisions")),
4771 (b'', b'all', False, _(b"list files from all revisions")),
4772 ]
4772 ]
4773 + formatteropts,
4773 + formatteropts,
4774 _(b'[-r REV]'),
4774 _(b'[-r REV]'),
4775 helpcategory=command.CATEGORY_MAINTENANCE,
4775 helpcategory=command.CATEGORY_MAINTENANCE,
4776 intents={INTENT_READONLY},
4776 intents={INTENT_READONLY},
4777 )
4777 )
4778 def manifest(ui, repo, node=None, rev=None, **opts):
4778 def manifest(ui, repo, node=None, rev=None, **opts):
4779 """output the current or given revision of the project manifest
4779 """output the current or given revision of the project manifest
4780
4780
4781 Print a list of version controlled files for the given revision.
4781 Print a list of version controlled files for the given revision.
4782 If no revision is given, the first parent of the working directory
4782 If no revision is given, the first parent of the working directory
4783 is used, or the null revision if no revision is checked out.
4783 is used, or the null revision if no revision is checked out.
4784
4784
4785 With -v, print file permissions, symlink and executable bits.
4785 With -v, print file permissions, symlink and executable bits.
4786 With --debug, print file revision hashes.
4786 With --debug, print file revision hashes.
4787
4787
4788 If option --all is specified, the list of all files from all revisions
4788 If option --all is specified, the list of all files from all revisions
4789 is printed. This includes deleted and renamed files.
4789 is printed. This includes deleted and renamed files.
4790
4790
4791 Returns 0 on success.
4791 Returns 0 on success.
4792 """
4792 """
4793 opts = pycompat.byteskwargs(opts)
4793 opts = pycompat.byteskwargs(opts)
4794 fm = ui.formatter(b'manifest', opts)
4794 fm = ui.formatter(b'manifest', opts)
4795
4795
4796 if opts.get(b'all'):
4796 if opts.get(b'all'):
4797 if rev or node:
4797 if rev or node:
4798 raise error.Abort(_(b"can't specify a revision with --all"))
4798 raise error.Abort(_(b"can't specify a revision with --all"))
4799
4799
4800 res = set()
4800 res = set()
4801 for rev in repo:
4801 for rev in repo:
4802 ctx = repo[rev]
4802 ctx = repo[rev]
4803 res |= set(ctx.files())
4803 res |= set(ctx.files())
4804
4804
4805 ui.pager(b'manifest')
4805 ui.pager(b'manifest')
4806 for f in sorted(res):
4806 for f in sorted(res):
4807 fm.startitem()
4807 fm.startitem()
4808 fm.write(b"path", b'%s\n', f)
4808 fm.write(b"path", b'%s\n', f)
4809 fm.end()
4809 fm.end()
4810 return
4810 return
4811
4811
4812 if rev and node:
4812 if rev and node:
4813 raise error.Abort(_(b"please specify just one revision"))
4813 raise error.Abort(_(b"please specify just one revision"))
4814
4814
4815 if not node:
4815 if not node:
4816 node = rev
4816 node = rev
4817
4817
4818 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4818 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4819 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4819 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4820 if node:
4820 if node:
4821 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4821 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4822 ctx = scmutil.revsingle(repo, node)
4822 ctx = scmutil.revsingle(repo, node)
4823 mf = ctx.manifest()
4823 mf = ctx.manifest()
4824 ui.pager(b'manifest')
4824 ui.pager(b'manifest')
4825 for f in ctx:
4825 for f in ctx:
4826 fm.startitem()
4826 fm.startitem()
4827 fm.context(ctx=ctx)
4827 fm.context(ctx=ctx)
4828 fl = ctx[f].flags()
4828 fl = ctx[f].flags()
4829 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4829 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4830 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4830 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4831 fm.write(b'path', b'%s\n', f)
4831 fm.write(b'path', b'%s\n', f)
4832 fm.end()
4832 fm.end()
4833
4833
4834
4834
4835 @command(
4835 @command(
4836 b'merge',
4836 b'merge',
4837 [
4837 [
4838 (
4838 (
4839 b'f',
4839 b'f',
4840 b'force',
4840 b'force',
4841 None,
4841 None,
4842 _(b'force a merge including outstanding changes (DEPRECATED)'),
4842 _(b'force a merge including outstanding changes (DEPRECATED)'),
4843 ),
4843 ),
4844 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4844 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4845 (
4845 (
4846 b'P',
4846 b'P',
4847 b'preview',
4847 b'preview',
4848 None,
4848 None,
4849 _(b'review revisions to merge (no merge is performed)'),
4849 _(b'review revisions to merge (no merge is performed)'),
4850 ),
4850 ),
4851 (b'', b'abort', None, _(b'abort the ongoing merge')),
4851 (b'', b'abort', None, _(b'abort the ongoing merge')),
4852 ]
4852 ]
4853 + mergetoolopts,
4853 + mergetoolopts,
4854 _(b'[-P] [[-r] REV]'),
4854 _(b'[-P] [[-r] REV]'),
4855 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4855 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4856 helpbasic=True,
4856 helpbasic=True,
4857 )
4857 )
4858 def merge(ui, repo, node=None, **opts):
4858 def merge(ui, repo, node=None, **opts):
4859 """merge another revision into working directory
4859 """merge another revision into working directory
4860
4860
4861 The current working directory is updated with all changes made in
4861 The current working directory is updated with all changes made in
4862 the requested revision since the last common predecessor revision.
4862 the requested revision since the last common predecessor revision.
4863
4863
4864 Files that changed between either parent are marked as changed for
4864 Files that changed between either parent are marked as changed for
4865 the next commit and a commit must be performed before any further
4865 the next commit and a commit must be performed before any further
4866 updates to the repository are allowed. The next commit will have
4866 updates to the repository are allowed. The next commit will have
4867 two parents.
4867 two parents.
4868
4868
4869 ``--tool`` can be used to specify the merge tool used for file
4869 ``--tool`` can be used to specify the merge tool used for file
4870 merges. It overrides the HGMERGE environment variable and your
4870 merges. It overrides the HGMERGE environment variable and your
4871 configuration files. See :hg:`help merge-tools` for options.
4871 configuration files. See :hg:`help merge-tools` for options.
4872
4872
4873 If no revision is specified, the working directory's parent is a
4873 If no revision is specified, the working directory's parent is a
4874 head revision, and the current branch contains exactly one other
4874 head revision, and the current branch contains exactly one other
4875 head, the other head is merged with by default. Otherwise, an
4875 head, the other head is merged with by default. Otherwise, an
4876 explicit revision with which to merge must be provided.
4876 explicit revision with which to merge must be provided.
4877
4877
4878 See :hg:`help resolve` for information on handling file conflicts.
4878 See :hg:`help resolve` for information on handling file conflicts.
4879
4879
4880 To undo an uncommitted merge, use :hg:`merge --abort` which
4880 To undo an uncommitted merge, use :hg:`merge --abort` which
4881 will check out a clean copy of the original merge parent, losing
4881 will check out a clean copy of the original merge parent, losing
4882 all changes.
4882 all changes.
4883
4883
4884 Returns 0 on success, 1 if there are unresolved files.
4884 Returns 0 on success, 1 if there are unresolved files.
4885 """
4885 """
4886
4886
4887 opts = pycompat.byteskwargs(opts)
4887 opts = pycompat.byteskwargs(opts)
4888 abort = opts.get(b'abort')
4888 abort = opts.get(b'abort')
4889 if abort and repo.dirstate.p2() == nullid:
4889 if abort and repo.dirstate.p2() == nullid:
4890 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4890 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4891 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4891 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4892 if abort:
4892 if abort:
4893 state = cmdutil.getunfinishedstate(repo)
4893 state = cmdutil.getunfinishedstate(repo)
4894 if state and state._opname != b'merge':
4894 if state and state._opname != b'merge':
4895 raise error.Abort(
4895 raise error.Abort(
4896 _(b'cannot abort merge with %s in progress') % (state._opname),
4896 _(b'cannot abort merge with %s in progress') % (state._opname),
4897 hint=state.hint(),
4897 hint=state.hint(),
4898 )
4898 )
4899 if node:
4899 if node:
4900 raise error.Abort(_(b"cannot specify a node with --abort"))
4900 raise error.Abort(_(b"cannot specify a node with --abort"))
4901 return hg.abortmerge(repo.ui, repo)
4901 return hg.abortmerge(repo.ui, repo)
4902
4902
4903 if opts.get(b'rev') and node:
4903 if opts.get(b'rev') and node:
4904 raise error.Abort(_(b"please specify just one revision"))
4904 raise error.Abort(_(b"please specify just one revision"))
4905 if not node:
4905 if not node:
4906 node = opts.get(b'rev')
4906 node = opts.get(b'rev')
4907
4907
4908 if node:
4908 if node:
4909 ctx = scmutil.revsingle(repo, node)
4909 ctx = scmutil.revsingle(repo, node)
4910 else:
4910 else:
4911 if ui.configbool(b'commands', b'merge.require-rev'):
4911 if ui.configbool(b'commands', b'merge.require-rev'):
4912 raise error.Abort(
4912 raise error.Abort(
4913 _(
4913 _(
4914 b'configuration requires specifying revision to merge '
4914 b'configuration requires specifying revision to merge '
4915 b'with'
4915 b'with'
4916 )
4916 )
4917 )
4917 )
4918 ctx = repo[destutil.destmerge(repo)]
4918 ctx = repo[destutil.destmerge(repo)]
4919
4919
4920 if ctx.node() is None:
4920 if ctx.node() is None:
4921 raise error.Abort(_(b'merging with the working copy has no effect'))
4921 raise error.Abort(_(b'merging with the working copy has no effect'))
4922
4922
4923 if opts.get(b'preview'):
4923 if opts.get(b'preview'):
4924 # find nodes that are ancestors of p2 but not of p1
4924 # find nodes that are ancestors of p2 but not of p1
4925 p1 = repo[b'.'].node()
4925 p1 = repo[b'.'].node()
4926 p2 = ctx.node()
4926 p2 = ctx.node()
4927 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4927 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4928
4928
4929 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4929 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4930 for node in nodes:
4930 for node in nodes:
4931 displayer.show(repo[node])
4931 displayer.show(repo[node])
4932 displayer.close()
4932 displayer.close()
4933 return 0
4933 return 0
4934
4934
4935 # ui.forcemerge is an internal variable, do not document
4935 # ui.forcemerge is an internal variable, do not document
4936 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4936 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4937 with ui.configoverride(overrides, b'merge'):
4937 with ui.configoverride(overrides, b'merge'):
4938 force = opts.get(b'force')
4938 force = opts.get(b'force')
4939 labels = [b'working copy', b'merge rev']
4939 labels = [b'working copy', b'merge rev']
4940 return hg.merge(ctx, force=force, labels=labels)
4940 return hg.merge(ctx, force=force, labels=labels)
4941
4941
4942
4942
4943 statemod.addunfinished(
4943 statemod.addunfinished(
4944 b'merge',
4944 b'merge',
4945 fname=None,
4945 fname=None,
4946 clearable=True,
4946 clearable=True,
4947 allowcommit=True,
4947 allowcommit=True,
4948 cmdmsg=_(b'outstanding uncommitted merge'),
4948 cmdmsg=_(b'outstanding uncommitted merge'),
4949 abortfunc=hg.abortmerge,
4949 abortfunc=hg.abortmerge,
4950 statushint=_(
4950 statushint=_(
4951 b'To continue: hg commit\nTo abort: hg merge --abort'
4951 b'To continue: hg commit\nTo abort: hg merge --abort'
4952 ),
4952 ),
4953 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4953 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4954 )
4954 )
4955
4955
4956
4956
4957 @command(
4957 @command(
4958 b'outgoing|out',
4958 b'outgoing|out',
4959 [
4959 [
4960 (
4960 (
4961 b'f',
4961 b'f',
4962 b'force',
4962 b'force',
4963 None,
4963 None,
4964 _(b'run even when the destination is unrelated'),
4964 _(b'run even when the destination is unrelated'),
4965 ),
4965 ),
4966 (
4966 (
4967 b'r',
4967 b'r',
4968 b'rev',
4968 b'rev',
4969 [],
4969 [],
4970 _(b'a changeset intended to be included in the destination'),
4970 _(b'a changeset intended to be included in the destination'),
4971 _(b'REV'),
4971 _(b'REV'),
4972 ),
4972 ),
4973 (b'n', b'newest-first', None, _(b'show newest record first')),
4973 (b'n', b'newest-first', None, _(b'show newest record first')),
4974 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4974 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4975 (
4975 (
4976 b'b',
4976 b'b',
4977 b'branch',
4977 b'branch',
4978 [],
4978 [],
4979 _(b'a specific branch you would like to push'),
4979 _(b'a specific branch you would like to push'),
4980 _(b'BRANCH'),
4980 _(b'BRANCH'),
4981 ),
4981 ),
4982 ]
4982 ]
4983 + logopts
4983 + logopts
4984 + remoteopts
4984 + remoteopts
4985 + subrepoopts,
4985 + subrepoopts,
4986 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4986 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4987 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4987 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4988 )
4988 )
4989 def outgoing(ui, repo, dest=None, **opts):
4989 def outgoing(ui, repo, dest=None, **opts):
4990 """show changesets not found in the destination
4990 """show changesets not found in the destination
4991
4991
4992 Show changesets not found in the specified destination repository
4992 Show changesets not found in the specified destination repository
4993 or the default push location. These are the changesets that would
4993 or the default push location. These are the changesets that would
4994 be pushed if a push was requested.
4994 be pushed if a push was requested.
4995
4995
4996 See pull for details of valid destination formats.
4996 See pull for details of valid destination formats.
4997
4997
4998 .. container:: verbose
4998 .. container:: verbose
4999
4999
5000 With -B/--bookmarks, the result of bookmark comparison between
5000 With -B/--bookmarks, the result of bookmark comparison between
5001 local and remote repositories is displayed. With -v/--verbose,
5001 local and remote repositories is displayed. With -v/--verbose,
5002 status is also displayed for each bookmark like below::
5002 status is also displayed for each bookmark like below::
5003
5003
5004 BM1 01234567890a added
5004 BM1 01234567890a added
5005 BM2 deleted
5005 BM2 deleted
5006 BM3 234567890abc advanced
5006 BM3 234567890abc advanced
5007 BM4 34567890abcd diverged
5007 BM4 34567890abcd diverged
5008 BM5 4567890abcde changed
5008 BM5 4567890abcde changed
5009
5009
5010 The action taken when pushing depends on the
5010 The action taken when pushing depends on the
5011 status of each bookmark:
5011 status of each bookmark:
5012
5012
5013 :``added``: push with ``-B`` will create it
5013 :``added``: push with ``-B`` will create it
5014 :``deleted``: push with ``-B`` will delete it
5014 :``deleted``: push with ``-B`` will delete it
5015 :``advanced``: push will update it
5015 :``advanced``: push will update it
5016 :``diverged``: push with ``-B`` will update it
5016 :``diverged``: push with ``-B`` will update it
5017 :``changed``: push with ``-B`` will update it
5017 :``changed``: push with ``-B`` will update it
5018
5018
5019 From the point of view of pushing behavior, bookmarks
5019 From the point of view of pushing behavior, bookmarks
5020 existing only in the remote repository are treated as
5020 existing only in the remote repository are treated as
5021 ``deleted``, even if it is in fact added remotely.
5021 ``deleted``, even if it is in fact added remotely.
5022
5022
5023 Returns 0 if there are outgoing changes, 1 otherwise.
5023 Returns 0 if there are outgoing changes, 1 otherwise.
5024 """
5024 """
5025 # hg._outgoing() needs to re-resolve the path in order to handle #branch
5025 # hg._outgoing() needs to re-resolve the path in order to handle #branch
5026 # style URLs, so don't overwrite dest.
5026 # style URLs, so don't overwrite dest.
5027 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5027 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5028 if not path:
5028 if not path:
5029 raise error.Abort(
5029 raise error.Abort(
5030 _(b'default repository not configured!'),
5030 _(b'default repository not configured!'),
5031 hint=_(b"see 'hg help config.paths'"),
5031 hint=_(b"see 'hg help config.paths'"),
5032 )
5032 )
5033
5033
5034 opts = pycompat.byteskwargs(opts)
5034 opts = pycompat.byteskwargs(opts)
5035 if opts.get(b'graph'):
5035 if opts.get(b'graph'):
5036 logcmdutil.checkunsupportedgraphflags([], opts)
5036 logcmdutil.checkunsupportedgraphflags([], opts)
5037 o, other = hg._outgoing(ui, repo, dest, opts)
5037 o, other = hg._outgoing(ui, repo, dest, opts)
5038 if not o:
5038 if not o:
5039 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5039 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5040 return
5040 return
5041
5041
5042 revdag = logcmdutil.graphrevs(repo, o, opts)
5042 revdag = logcmdutil.graphrevs(repo, o, opts)
5043 ui.pager(b'outgoing')
5043 ui.pager(b'outgoing')
5044 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5044 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5045 logcmdutil.displaygraph(
5045 logcmdutil.displaygraph(
5046 ui, repo, revdag, displayer, graphmod.asciiedges
5046 ui, repo, revdag, displayer, graphmod.asciiedges
5047 )
5047 )
5048 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5048 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5049 return 0
5049 return 0
5050
5050
5051 if opts.get(b'bookmarks'):
5051 if opts.get(b'bookmarks'):
5052 dest = path.pushloc or path.loc
5052 dest = path.pushloc or path.loc
5053 other = hg.peer(repo, opts, dest)
5053 other = hg.peer(repo, opts, dest)
5054 if b'bookmarks' not in other.listkeys(b'namespaces'):
5054 if b'bookmarks' not in other.listkeys(b'namespaces'):
5055 ui.warn(_(b"remote doesn't support bookmarks\n"))
5055 ui.warn(_(b"remote doesn't support bookmarks\n"))
5056 return 0
5056 return 0
5057 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5057 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5058 ui.pager(b'outgoing')
5058 ui.pager(b'outgoing')
5059 return bookmarks.outgoing(ui, repo, other)
5059 return bookmarks.outgoing(ui, repo, other)
5060
5060
5061 repo._subtoppath = path.pushloc or path.loc
5061 repo._subtoppath = path.pushloc or path.loc
5062 try:
5062 try:
5063 return hg.outgoing(ui, repo, dest, opts)
5063 return hg.outgoing(ui, repo, dest, opts)
5064 finally:
5064 finally:
5065 del repo._subtoppath
5065 del repo._subtoppath
5066
5066
5067
5067
5068 @command(
5068 @command(
5069 b'parents',
5069 b'parents',
5070 [
5070 [
5071 (
5071 (
5072 b'r',
5072 b'r',
5073 b'rev',
5073 b'rev',
5074 b'',
5074 b'',
5075 _(b'show parents of the specified revision'),
5075 _(b'show parents of the specified revision'),
5076 _(b'REV'),
5076 _(b'REV'),
5077 ),
5077 ),
5078 ]
5078 ]
5079 + templateopts,
5079 + templateopts,
5080 _(b'[-r REV] [FILE]'),
5080 _(b'[-r REV] [FILE]'),
5081 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5081 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5082 inferrepo=True,
5082 inferrepo=True,
5083 )
5083 )
5084 def parents(ui, repo, file_=None, **opts):
5084 def parents(ui, repo, file_=None, **opts):
5085 """show the parents of the working directory or revision (DEPRECATED)
5085 """show the parents of the working directory or revision (DEPRECATED)
5086
5086
5087 Print the working directory's parent revisions. If a revision is
5087 Print the working directory's parent revisions. If a revision is
5088 given via -r/--rev, the parent of that revision will be printed.
5088 given via -r/--rev, the parent of that revision will be printed.
5089 If a file argument is given, the revision in which the file was
5089 If a file argument is given, the revision in which the file was
5090 last changed (before the working directory revision or the
5090 last changed (before the working directory revision or the
5091 argument to --rev if given) is printed.
5091 argument to --rev if given) is printed.
5092
5092
5093 This command is equivalent to::
5093 This command is equivalent to::
5094
5094
5095 hg log -r "p1()+p2()" or
5095 hg log -r "p1()+p2()" or
5096 hg log -r "p1(REV)+p2(REV)" or
5096 hg log -r "p1(REV)+p2(REV)" or
5097 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5097 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5098 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5098 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5099
5099
5100 See :hg:`summary` and :hg:`help revsets` for related information.
5100 See :hg:`summary` and :hg:`help revsets` for related information.
5101
5101
5102 Returns 0 on success.
5102 Returns 0 on success.
5103 """
5103 """
5104
5104
5105 opts = pycompat.byteskwargs(opts)
5105 opts = pycompat.byteskwargs(opts)
5106 rev = opts.get(b'rev')
5106 rev = opts.get(b'rev')
5107 if rev:
5107 if rev:
5108 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5108 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5109 ctx = scmutil.revsingle(repo, rev, None)
5109 ctx = scmutil.revsingle(repo, rev, None)
5110
5110
5111 if file_:
5111 if file_:
5112 m = scmutil.match(ctx, (file_,), opts)
5112 m = scmutil.match(ctx, (file_,), opts)
5113 if m.anypats() or len(m.files()) != 1:
5113 if m.anypats() or len(m.files()) != 1:
5114 raise error.Abort(_(b'can only specify an explicit filename'))
5114 raise error.Abort(_(b'can only specify an explicit filename'))
5115 file_ = m.files()[0]
5115 file_ = m.files()[0]
5116 filenodes = []
5116 filenodes = []
5117 for cp in ctx.parents():
5117 for cp in ctx.parents():
5118 if not cp:
5118 if not cp:
5119 continue
5119 continue
5120 try:
5120 try:
5121 filenodes.append(cp.filenode(file_))
5121 filenodes.append(cp.filenode(file_))
5122 except error.LookupError:
5122 except error.LookupError:
5123 pass
5123 pass
5124 if not filenodes:
5124 if not filenodes:
5125 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5125 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5126 p = []
5126 p = []
5127 for fn in filenodes:
5127 for fn in filenodes:
5128 fctx = repo.filectx(file_, fileid=fn)
5128 fctx = repo.filectx(file_, fileid=fn)
5129 p.append(fctx.node())
5129 p.append(fctx.node())
5130 else:
5130 else:
5131 p = [cp.node() for cp in ctx.parents()]
5131 p = [cp.node() for cp in ctx.parents()]
5132
5132
5133 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5133 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5134 for n in p:
5134 for n in p:
5135 if n != nullid:
5135 if n != nullid:
5136 displayer.show(repo[n])
5136 displayer.show(repo[n])
5137 displayer.close()
5137 displayer.close()
5138
5138
5139
5139
5140 @command(
5140 @command(
5141 b'paths',
5141 b'paths',
5142 formatteropts,
5142 formatteropts,
5143 _(b'[NAME]'),
5143 _(b'[NAME]'),
5144 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5144 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5145 optionalrepo=True,
5145 optionalrepo=True,
5146 intents={INTENT_READONLY},
5146 intents={INTENT_READONLY},
5147 )
5147 )
5148 def paths(ui, repo, search=None, **opts):
5148 def paths(ui, repo, search=None, **opts):
5149 """show aliases for remote repositories
5149 """show aliases for remote repositories
5150
5150
5151 Show definition of symbolic path name NAME. If no name is given,
5151 Show definition of symbolic path name NAME. If no name is given,
5152 show definition of all available names.
5152 show definition of all available names.
5153
5153
5154 Option -q/--quiet suppresses all output when searching for NAME
5154 Option -q/--quiet suppresses all output when searching for NAME
5155 and shows only the path names when listing all definitions.
5155 and shows only the path names when listing all definitions.
5156
5156
5157 Path names are defined in the [paths] section of your
5157 Path names are defined in the [paths] section of your
5158 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5158 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5159 repository, ``.hg/hgrc`` is used, too.
5159 repository, ``.hg/hgrc`` is used, too.
5160
5160
5161 The path names ``default`` and ``default-push`` have a special
5161 The path names ``default`` and ``default-push`` have a special
5162 meaning. When performing a push or pull operation, they are used
5162 meaning. When performing a push or pull operation, they are used
5163 as fallbacks if no location is specified on the command-line.
5163 as fallbacks if no location is specified on the command-line.
5164 When ``default-push`` is set, it will be used for push and
5164 When ``default-push`` is set, it will be used for push and
5165 ``default`` will be used for pull; otherwise ``default`` is used
5165 ``default`` will be used for pull; otherwise ``default`` is used
5166 as the fallback for both. When cloning a repository, the clone
5166 as the fallback for both. When cloning a repository, the clone
5167 source is written as ``default`` in ``.hg/hgrc``.
5167 source is written as ``default`` in ``.hg/hgrc``.
5168
5168
5169 .. note::
5169 .. note::
5170
5170
5171 ``default`` and ``default-push`` apply to all inbound (e.g.
5171 ``default`` and ``default-push`` apply to all inbound (e.g.
5172 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5172 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5173 and :hg:`bundle`) operations.
5173 and :hg:`bundle`) operations.
5174
5174
5175 See :hg:`help urls` for more information.
5175 See :hg:`help urls` for more information.
5176
5176
5177 .. container:: verbose
5177 .. container:: verbose
5178
5178
5179 Template:
5179 Template:
5180
5180
5181 The following keywords are supported. See also :hg:`help templates`.
5181 The following keywords are supported. See also :hg:`help templates`.
5182
5182
5183 :name: String. Symbolic name of the path alias.
5183 :name: String. Symbolic name of the path alias.
5184 :pushurl: String. URL for push operations.
5184 :pushurl: String. URL for push operations.
5185 :url: String. URL or directory path for the other operations.
5185 :url: String. URL or directory path for the other operations.
5186
5186
5187 Returns 0 on success.
5187 Returns 0 on success.
5188 """
5188 """
5189
5189
5190 opts = pycompat.byteskwargs(opts)
5190 opts = pycompat.byteskwargs(opts)
5191 ui.pager(b'paths')
5191 ui.pager(b'paths')
5192 if search:
5192 if search:
5193 pathitems = [
5193 pathitems = [
5194 (name, path)
5194 (name, path)
5195 for name, path in pycompat.iteritems(ui.paths)
5195 for name, path in pycompat.iteritems(ui.paths)
5196 if name == search
5196 if name == search
5197 ]
5197 ]
5198 else:
5198 else:
5199 pathitems = sorted(pycompat.iteritems(ui.paths))
5199 pathitems = sorted(pycompat.iteritems(ui.paths))
5200
5200
5201 fm = ui.formatter(b'paths', opts)
5201 fm = ui.formatter(b'paths', opts)
5202 if fm.isplain():
5202 if fm.isplain():
5203 hidepassword = util.hidepassword
5203 hidepassword = util.hidepassword
5204 else:
5204 else:
5205 hidepassword = bytes
5205 hidepassword = bytes
5206 if ui.quiet:
5206 if ui.quiet:
5207 namefmt = b'%s\n'
5207 namefmt = b'%s\n'
5208 else:
5208 else:
5209 namefmt = b'%s = '
5209 namefmt = b'%s = '
5210 showsubopts = not search and not ui.quiet
5210 showsubopts = not search and not ui.quiet
5211
5211
5212 for name, path in pathitems:
5212 for name, path in pathitems:
5213 fm.startitem()
5213 fm.startitem()
5214 fm.condwrite(not search, b'name', namefmt, name)
5214 fm.condwrite(not search, b'name', namefmt, name)
5215 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5215 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5216 for subopt, value in sorted(path.suboptions.items()):
5216 for subopt, value in sorted(path.suboptions.items()):
5217 assert subopt not in (b'name', b'url')
5217 assert subopt not in (b'name', b'url')
5218 if showsubopts:
5218 if showsubopts:
5219 fm.plain(b'%s:%s = ' % (name, subopt))
5219 fm.plain(b'%s:%s = ' % (name, subopt))
5220 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5220 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5221
5221
5222 fm.end()
5222 fm.end()
5223
5223
5224 if search and not pathitems:
5224 if search and not pathitems:
5225 if not ui.quiet:
5225 if not ui.quiet:
5226 ui.warn(_(b"not found!\n"))
5226 ui.warn(_(b"not found!\n"))
5227 return 1
5227 return 1
5228 else:
5228 else:
5229 return 0
5229 return 0
5230
5230
5231
5231
5232 @command(
5232 @command(
5233 b'phase',
5233 b'phase',
5234 [
5234 [
5235 (b'p', b'public', False, _(b'set changeset phase to public')),
5235 (b'p', b'public', False, _(b'set changeset phase to public')),
5236 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5236 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5237 (b's', b'secret', False, _(b'set changeset phase to secret')),
5237 (b's', b'secret', False, _(b'set changeset phase to secret')),
5238 (b'f', b'force', False, _(b'allow to move boundary backward')),
5238 (b'f', b'force', False, _(b'allow to move boundary backward')),
5239 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5239 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5240 ],
5240 ],
5241 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5241 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5242 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5242 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5243 )
5243 )
5244 def phase(ui, repo, *revs, **opts):
5244 def phase(ui, repo, *revs, **opts):
5245 """set or show the current phase name
5245 """set or show the current phase name
5246
5246
5247 With no argument, show the phase name of the current revision(s).
5247 With no argument, show the phase name of the current revision(s).
5248
5248
5249 With one of -p/--public, -d/--draft or -s/--secret, change the
5249 With one of -p/--public, -d/--draft or -s/--secret, change the
5250 phase value of the specified revisions.
5250 phase value of the specified revisions.
5251
5251
5252 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5252 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5253 lower phase to a higher phase. Phases are ordered as follows::
5253 lower phase to a higher phase. Phases are ordered as follows::
5254
5254
5255 public < draft < secret
5255 public < draft < secret
5256
5256
5257 Returns 0 on success, 1 if some phases could not be changed.
5257 Returns 0 on success, 1 if some phases could not be changed.
5258
5258
5259 (For more information about the phases concept, see :hg:`help phases`.)
5259 (For more information about the phases concept, see :hg:`help phases`.)
5260 """
5260 """
5261 opts = pycompat.byteskwargs(opts)
5261 opts = pycompat.byteskwargs(opts)
5262 # search for a unique phase argument
5262 # search for a unique phase argument
5263 targetphase = None
5263 targetphase = None
5264 for idx, name in enumerate(phases.cmdphasenames):
5264 for idx, name in enumerate(phases.cmdphasenames):
5265 if opts[name]:
5265 if opts[name]:
5266 if targetphase is not None:
5266 if targetphase is not None:
5267 raise error.Abort(_(b'only one phase can be specified'))
5267 raise error.Abort(_(b'only one phase can be specified'))
5268 targetphase = idx
5268 targetphase = idx
5269
5269
5270 # look for specified revision
5270 # look for specified revision
5271 revs = list(revs)
5271 revs = list(revs)
5272 revs.extend(opts[b'rev'])
5272 revs.extend(opts[b'rev'])
5273 if not revs:
5273 if not revs:
5274 # display both parents as the second parent phase can influence
5274 # display both parents as the second parent phase can influence
5275 # the phase of a merge commit
5275 # the phase of a merge commit
5276 revs = [c.rev() for c in repo[None].parents()]
5276 revs = [c.rev() for c in repo[None].parents()]
5277
5277
5278 revs = scmutil.revrange(repo, revs)
5278 revs = scmutil.revrange(repo, revs)
5279
5279
5280 ret = 0
5280 ret = 0
5281 if targetphase is None:
5281 if targetphase is None:
5282 # display
5282 # display
5283 for r in revs:
5283 for r in revs:
5284 ctx = repo[r]
5284 ctx = repo[r]
5285 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5285 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5286 else:
5286 else:
5287 with repo.lock(), repo.transaction(b"phase") as tr:
5287 with repo.lock(), repo.transaction(b"phase") as tr:
5288 # set phase
5288 # set phase
5289 if not revs:
5289 if not revs:
5290 raise error.Abort(_(b'empty revision set'))
5290 raise error.Abort(_(b'empty revision set'))
5291 nodes = [repo[r].node() for r in revs]
5291 nodes = [repo[r].node() for r in revs]
5292 # moving revision from public to draft may hide them
5292 # moving revision from public to draft may hide them
5293 # We have to check result on an unfiltered repository
5293 # We have to check result on an unfiltered repository
5294 unfi = repo.unfiltered()
5294 unfi = repo.unfiltered()
5295 getphase = unfi._phasecache.phase
5295 getphase = unfi._phasecache.phase
5296 olddata = [getphase(unfi, r) for r in unfi]
5296 olddata = [getphase(unfi, r) for r in unfi]
5297 phases.advanceboundary(repo, tr, targetphase, nodes)
5297 phases.advanceboundary(repo, tr, targetphase, nodes)
5298 if opts[b'force']:
5298 if opts[b'force']:
5299 phases.retractboundary(repo, tr, targetphase, nodes)
5299 phases.retractboundary(repo, tr, targetphase, nodes)
5300 getphase = unfi._phasecache.phase
5300 getphase = unfi._phasecache.phase
5301 newdata = [getphase(unfi, r) for r in unfi]
5301 newdata = [getphase(unfi, r) for r in unfi]
5302 changes = sum(newdata[r] != olddata[r] for r in unfi)
5302 changes = sum(newdata[r] != olddata[r] for r in unfi)
5303 cl = unfi.changelog
5303 cl = unfi.changelog
5304 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5304 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5305 if rejected:
5305 if rejected:
5306 ui.warn(
5306 ui.warn(
5307 _(
5307 _(
5308 b'cannot move %i changesets to a higher '
5308 b'cannot move %i changesets to a higher '
5309 b'phase, use --force\n'
5309 b'phase, use --force\n'
5310 )
5310 )
5311 % len(rejected)
5311 % len(rejected)
5312 )
5312 )
5313 ret = 1
5313 ret = 1
5314 if changes:
5314 if changes:
5315 msg = _(b'phase changed for %i changesets\n') % changes
5315 msg = _(b'phase changed for %i changesets\n') % changes
5316 if ret:
5316 if ret:
5317 ui.status(msg)
5317 ui.status(msg)
5318 else:
5318 else:
5319 ui.note(msg)
5319 ui.note(msg)
5320 else:
5320 else:
5321 ui.warn(_(b'no phases changed\n'))
5321 ui.warn(_(b'no phases changed\n'))
5322 return ret
5322 return ret
5323
5323
5324
5324
5325 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5325 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5326 """Run after a changegroup has been added via pull/unbundle
5326 """Run after a changegroup has been added via pull/unbundle
5327
5327
5328 This takes arguments below:
5328 This takes arguments below:
5329
5329
5330 :modheads: change of heads by pull/unbundle
5330 :modheads: change of heads by pull/unbundle
5331 :optupdate: updating working directory is needed or not
5331 :optupdate: updating working directory is needed or not
5332 :checkout: update destination revision (or None to default destination)
5332 :checkout: update destination revision (or None to default destination)
5333 :brev: a name, which might be a bookmark to be activated after updating
5333 :brev: a name, which might be a bookmark to be activated after updating
5334 """
5334 """
5335 if modheads == 0:
5335 if modheads == 0:
5336 return
5336 return
5337 if optupdate:
5337 if optupdate:
5338 try:
5338 try:
5339 return hg.updatetotally(ui, repo, checkout, brev)
5339 return hg.updatetotally(ui, repo, checkout, brev)
5340 except error.UpdateAbort as inst:
5340 except error.UpdateAbort as inst:
5341 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5341 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5342 hint = inst.hint
5342 hint = inst.hint
5343 raise error.UpdateAbort(msg, hint=hint)
5343 raise error.UpdateAbort(msg, hint=hint)
5344 if modheads is not None and modheads > 1:
5344 if modheads is not None and modheads > 1:
5345 currentbranchheads = len(repo.branchheads())
5345 currentbranchheads = len(repo.branchheads())
5346 if currentbranchheads == modheads:
5346 if currentbranchheads == modheads:
5347 ui.status(
5347 ui.status(
5348 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5348 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5349 )
5349 )
5350 elif currentbranchheads > 1:
5350 elif currentbranchheads > 1:
5351 ui.status(
5351 ui.status(
5352 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5352 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5353 )
5353 )
5354 else:
5354 else:
5355 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5355 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5356 elif not ui.configbool(b'commands', b'update.requiredest'):
5356 elif not ui.configbool(b'commands', b'update.requiredest'):
5357 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5357 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5358
5358
5359
5359
5360 @command(
5360 @command(
5361 b'pull',
5361 b'pull',
5362 [
5362 [
5363 (
5363 (
5364 b'u',
5364 b'u',
5365 b'update',
5365 b'update',
5366 None,
5366 None,
5367 _(b'update to new branch head if new descendants were pulled'),
5367 _(b'update to new branch head if new descendants were pulled'),
5368 ),
5368 ),
5369 (
5369 (
5370 b'f',
5370 b'f',
5371 b'force',
5371 b'force',
5372 None,
5372 None,
5373 _(b'run even when remote repository is unrelated'),
5373 _(b'run even when remote repository is unrelated'),
5374 ),
5374 ),
5375 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5375 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5376 (
5376 (
5377 b'r',
5377 b'r',
5378 b'rev',
5378 b'rev',
5379 [],
5379 [],
5380 _(b'a remote changeset intended to be added'),
5380 _(b'a remote changeset intended to be added'),
5381 _(b'REV'),
5381 _(b'REV'),
5382 ),
5382 ),
5383 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5383 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5384 (
5384 (
5385 b'b',
5385 b'b',
5386 b'branch',
5386 b'branch',
5387 [],
5387 [],
5388 _(b'a specific branch you would like to pull'),
5388 _(b'a specific branch you would like to pull'),
5389 _(b'BRANCH'),
5389 _(b'BRANCH'),
5390 ),
5390 ),
5391 ]
5391 ]
5392 + remoteopts,
5392 + remoteopts,
5393 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5393 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5394 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5394 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5395 helpbasic=True,
5395 helpbasic=True,
5396 )
5396 )
5397 def pull(ui, repo, source=b"default", **opts):
5397 def pull(ui, repo, source=b"default", **opts):
5398 """pull changes from the specified source
5398 """pull changes from the specified source
5399
5399
5400 Pull changes from a remote repository to a local one.
5400 Pull changes from a remote repository to a local one.
5401
5401
5402 This finds all changes from the repository at the specified path
5402 This finds all changes from the repository at the specified path
5403 or URL and adds them to a local repository (the current one unless
5403 or URL and adds them to a local repository (the current one unless
5404 -R is specified). By default, this does not update the copy of the
5404 -R is specified). By default, this does not update the copy of the
5405 project in the working directory.
5405 project in the working directory.
5406
5406
5407 When cloning from servers that support it, Mercurial may fetch
5407 When cloning from servers that support it, Mercurial may fetch
5408 pre-generated data. When this is done, hooks operating on incoming
5408 pre-generated data. When this is done, hooks operating on incoming
5409 changesets and changegroups may fire more than once, once for each
5409 changesets and changegroups may fire more than once, once for each
5410 pre-generated bundle and as well as for any additional remaining
5410 pre-generated bundle and as well as for any additional remaining
5411 data. See :hg:`help -e clonebundles` for more.
5411 data. See :hg:`help -e clonebundles` for more.
5412
5412
5413 Use :hg:`incoming` if you want to see what would have been added
5413 Use :hg:`incoming` if you want to see what would have been added
5414 by a pull at the time you issued this command. If you then decide
5414 by a pull at the time you issued this command. If you then decide
5415 to add those changes to the repository, you should use :hg:`pull
5415 to add those changes to the repository, you should use :hg:`pull
5416 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5416 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5417
5417
5418 If SOURCE is omitted, the 'default' path will be used.
5418 If SOURCE is omitted, the 'default' path will be used.
5419 See :hg:`help urls` for more information.
5419 See :hg:`help urls` for more information.
5420
5420
5421 Specifying bookmark as ``.`` is equivalent to specifying the active
5421 Specifying bookmark as ``.`` is equivalent to specifying the active
5422 bookmark's name.
5422 bookmark's name.
5423
5423
5424 Returns 0 on success, 1 if an update had unresolved files.
5424 Returns 0 on success, 1 if an update had unresolved files.
5425 """
5425 """
5426
5426
5427 opts = pycompat.byteskwargs(opts)
5427 opts = pycompat.byteskwargs(opts)
5428 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5428 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5429 b'update'
5429 b'update'
5430 ):
5430 ):
5431 msg = _(b'update destination required by configuration')
5431 msg = _(b'update destination required by configuration')
5432 hint = _(b'use hg pull followed by hg update DEST')
5432 hint = _(b'use hg pull followed by hg update DEST')
5433 raise error.Abort(msg, hint=hint)
5433 raise error.Abort(msg, hint=hint)
5434
5434
5435 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5435 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5436 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5436 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5437 other = hg.peer(repo, opts, source)
5437 other = hg.peer(repo, opts, source)
5438 try:
5438 try:
5439 revs, checkout = hg.addbranchrevs(
5439 revs, checkout = hg.addbranchrevs(
5440 repo, other, branches, opts.get(b'rev')
5440 repo, other, branches, opts.get(b'rev')
5441 )
5441 )
5442
5442
5443 pullopargs = {}
5443 pullopargs = {}
5444
5444
5445 nodes = None
5445 nodes = None
5446 if opts.get(b'bookmark') or revs:
5446 if opts.get(b'bookmark') or revs:
5447 # The list of bookmark used here is the same used to actually update
5447 # The list of bookmark used here is the same used to actually update
5448 # the bookmark names, to avoid the race from issue 4689 and we do
5448 # the bookmark names, to avoid the race from issue 4689 and we do
5449 # all lookup and bookmark queries in one go so they see the same
5449 # all lookup and bookmark queries in one go so they see the same
5450 # version of the server state (issue 4700).
5450 # version of the server state (issue 4700).
5451 nodes = []
5451 nodes = []
5452 fnodes = []
5452 fnodes = []
5453 revs = revs or []
5453 revs = revs or []
5454 if revs and not other.capable(b'lookup'):
5454 if revs and not other.capable(b'lookup'):
5455 err = _(
5455 err = _(
5456 b"other repository doesn't support revision lookup, "
5456 b"other repository doesn't support revision lookup, "
5457 b"so a rev cannot be specified."
5457 b"so a rev cannot be specified."
5458 )
5458 )
5459 raise error.Abort(err)
5459 raise error.Abort(err)
5460 with other.commandexecutor() as e:
5460 with other.commandexecutor() as e:
5461 fremotebookmarks = e.callcommand(
5461 fremotebookmarks = e.callcommand(
5462 b'listkeys', {b'namespace': b'bookmarks'}
5462 b'listkeys', {b'namespace': b'bookmarks'}
5463 )
5463 )
5464 for r in revs:
5464 for r in revs:
5465 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5465 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5466 remotebookmarks = fremotebookmarks.result()
5466 remotebookmarks = fremotebookmarks.result()
5467 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5467 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5468 pullopargs[b'remotebookmarks'] = remotebookmarks
5468 pullopargs[b'remotebookmarks'] = remotebookmarks
5469 for b in opts.get(b'bookmark', []):
5469 for b in opts.get(b'bookmark', []):
5470 b = repo._bookmarks.expandname(b)
5470 b = repo._bookmarks.expandname(b)
5471 if b not in remotebookmarks:
5471 if b not in remotebookmarks:
5472 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5472 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5473 nodes.append(remotebookmarks[b])
5473 nodes.append(remotebookmarks[b])
5474 for i, rev in enumerate(revs):
5474 for i, rev in enumerate(revs):
5475 node = fnodes[i].result()
5475 node = fnodes[i].result()
5476 nodes.append(node)
5476 nodes.append(node)
5477 if rev == checkout:
5477 if rev == checkout:
5478 checkout = node
5478 checkout = node
5479
5479
5480 wlock = util.nullcontextmanager()
5480 wlock = util.nullcontextmanager()
5481 if opts.get(b'update'):
5481 if opts.get(b'update'):
5482 wlock = repo.wlock()
5482 wlock = repo.wlock()
5483 with wlock:
5483 with wlock:
5484 pullopargs.update(opts.get(b'opargs', {}))
5484 pullopargs.update(opts.get(b'opargs', {}))
5485 modheads = exchange.pull(
5485 modheads = exchange.pull(
5486 repo,
5486 repo,
5487 other,
5487 other,
5488 heads=nodes,
5488 heads=nodes,
5489 force=opts.get(b'force'),
5489 force=opts.get(b'force'),
5490 bookmarks=opts.get(b'bookmark', ()),
5490 bookmarks=opts.get(b'bookmark', ()),
5491 opargs=pullopargs,
5491 opargs=pullopargs,
5492 confirm=opts.get(b'confirm'),
5492 confirm=opts.get(b'confirm'),
5493 ).cgresult
5493 ).cgresult
5494
5494
5495 # brev is a name, which might be a bookmark to be activated at
5495 # brev is a name, which might be a bookmark to be activated at
5496 # the end of the update. In other words, it is an explicit
5496 # the end of the update. In other words, it is an explicit
5497 # destination of the update
5497 # destination of the update
5498 brev = None
5498 brev = None
5499
5499
5500 if checkout:
5500 if checkout:
5501 checkout = repo.unfiltered().changelog.rev(checkout)
5501 checkout = repo.unfiltered().changelog.rev(checkout)
5502
5502
5503 # order below depends on implementation of
5503 # order below depends on implementation of
5504 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5504 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5505 # because 'checkout' is determined without it.
5505 # because 'checkout' is determined without it.
5506 if opts.get(b'rev'):
5506 if opts.get(b'rev'):
5507 brev = opts[b'rev'][0]
5507 brev = opts[b'rev'][0]
5508 elif opts.get(b'branch'):
5508 elif opts.get(b'branch'):
5509 brev = opts[b'branch'][0]
5509 brev = opts[b'branch'][0]
5510 else:
5510 else:
5511 brev = branches[0]
5511 brev = branches[0]
5512 repo._subtoppath = source
5512 repo._subtoppath = source
5513 try:
5513 try:
5514 ret = postincoming(
5514 ret = postincoming(
5515 ui, repo, modheads, opts.get(b'update'), checkout, brev
5515 ui, repo, modheads, opts.get(b'update'), checkout, brev
5516 )
5516 )
5517 except error.FilteredRepoLookupError as exc:
5517 except error.FilteredRepoLookupError as exc:
5518 msg = _(b'cannot update to target: %s') % exc.args[0]
5518 msg = _(b'cannot update to target: %s') % exc.args[0]
5519 exc.args = (msg,) + exc.args[1:]
5519 exc.args = (msg,) + exc.args[1:]
5520 raise
5520 raise
5521 finally:
5521 finally:
5522 del repo._subtoppath
5522 del repo._subtoppath
5523
5523
5524 finally:
5524 finally:
5525 other.close()
5525 other.close()
5526 return ret
5526 return ret
5527
5527
5528
5528
5529 @command(
5529 @command(
5530 b'push',
5530 b'push',
5531 [
5531 [
5532 (b'f', b'force', None, _(b'force push')),
5532 (b'f', b'force', None, _(b'force push')),
5533 (
5533 (
5534 b'r',
5534 b'r',
5535 b'rev',
5535 b'rev',
5536 [],
5536 [],
5537 _(b'a changeset intended to be included in the destination'),
5537 _(b'a changeset intended to be included in the destination'),
5538 _(b'REV'),
5538 _(b'REV'),
5539 ),
5539 ),
5540 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5540 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5541 (
5541 (
5542 b'b',
5542 b'b',
5543 b'branch',
5543 b'branch',
5544 [],
5544 [],
5545 _(b'a specific branch you would like to push'),
5545 _(b'a specific branch you would like to push'),
5546 _(b'BRANCH'),
5546 _(b'BRANCH'),
5547 ),
5547 ),
5548 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5548 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5549 (
5549 (
5550 b'',
5550 b'',
5551 b'pushvars',
5551 b'pushvars',
5552 [],
5552 [],
5553 _(b'variables that can be sent to server (ADVANCED)'),
5553 _(b'variables that can be sent to server (ADVANCED)'),
5554 ),
5554 ),
5555 (
5555 (
5556 b'',
5556 b'',
5557 b'publish',
5557 b'publish',
5558 False,
5558 False,
5559 _(b'push the changeset as public (EXPERIMENTAL)'),
5559 _(b'push the changeset as public (EXPERIMENTAL)'),
5560 ),
5560 ),
5561 ]
5561 ]
5562 + remoteopts,
5562 + remoteopts,
5563 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5563 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5564 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5564 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5565 helpbasic=True,
5565 helpbasic=True,
5566 )
5566 )
5567 def push(ui, repo, dest=None, **opts):
5567 def push(ui, repo, dest=None, **opts):
5568 """push changes to the specified destination
5568 """push changes to the specified destination
5569
5569
5570 Push changesets from the local repository to the specified
5570 Push changesets from the local repository to the specified
5571 destination.
5571 destination.
5572
5572
5573 This operation is symmetrical to pull: it is identical to a pull
5573 This operation is symmetrical to pull: it is identical to a pull
5574 in the destination repository from the current one.
5574 in the destination repository from the current one.
5575
5575
5576 By default, push will not allow creation of new heads at the
5576 By default, push will not allow creation of new heads at the
5577 destination, since multiple heads would make it unclear which head
5577 destination, since multiple heads would make it unclear which head
5578 to use. In this situation, it is recommended to pull and merge
5578 to use. In this situation, it is recommended to pull and merge
5579 before pushing.
5579 before pushing.
5580
5580
5581 Use --new-branch if you want to allow push to create a new named
5581 Use --new-branch if you want to allow push to create a new named
5582 branch that is not present at the destination. This allows you to
5582 branch that is not present at the destination. This allows you to
5583 only create a new branch without forcing other changes.
5583 only create a new branch without forcing other changes.
5584
5584
5585 .. note::
5585 .. note::
5586
5586
5587 Extra care should be taken with the -f/--force option,
5587 Extra care should be taken with the -f/--force option,
5588 which will push all new heads on all branches, an action which will
5588 which will push all new heads on all branches, an action which will
5589 almost always cause confusion for collaborators.
5589 almost always cause confusion for collaborators.
5590
5590
5591 If -r/--rev is used, the specified revision and all its ancestors
5591 If -r/--rev is used, the specified revision and all its ancestors
5592 will be pushed to the remote repository.
5592 will be pushed to the remote repository.
5593
5593
5594 If -B/--bookmark is used, the specified bookmarked revision, its
5594 If -B/--bookmark is used, the specified bookmarked revision, its
5595 ancestors, and the bookmark will be pushed to the remote
5595 ancestors, and the bookmark will be pushed to the remote
5596 repository. Specifying ``.`` is equivalent to specifying the active
5596 repository. Specifying ``.`` is equivalent to specifying the active
5597 bookmark's name.
5597 bookmark's name.
5598
5598
5599 Please see :hg:`help urls` for important details about ``ssh://``
5599 Please see :hg:`help urls` for important details about ``ssh://``
5600 URLs. If DESTINATION is omitted, a default path will be used.
5600 URLs. If DESTINATION is omitted, a default path will be used.
5601
5601
5602 .. container:: verbose
5602 .. container:: verbose
5603
5603
5604 The --pushvars option sends strings to the server that become
5604 The --pushvars option sends strings to the server that become
5605 environment variables prepended with ``HG_USERVAR_``. For example,
5605 environment variables prepended with ``HG_USERVAR_``. For example,
5606 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5606 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5607 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5607 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5608
5608
5609 pushvars can provide for user-overridable hooks as well as set debug
5609 pushvars can provide for user-overridable hooks as well as set debug
5610 levels. One example is having a hook that blocks commits containing
5610 levels. One example is having a hook that blocks commits containing
5611 conflict markers, but enables the user to override the hook if the file
5611 conflict markers, but enables the user to override the hook if the file
5612 is using conflict markers for testing purposes or the file format has
5612 is using conflict markers for testing purposes or the file format has
5613 strings that look like conflict markers.
5613 strings that look like conflict markers.
5614
5614
5615 By default, servers will ignore `--pushvars`. To enable it add the
5615 By default, servers will ignore `--pushvars`. To enable it add the
5616 following to your configuration file::
5616 following to your configuration file::
5617
5617
5618 [push]
5618 [push]
5619 pushvars.server = true
5619 pushvars.server = true
5620
5620
5621 Returns 0 if push was successful, 1 if nothing to push.
5621 Returns 0 if push was successful, 1 if nothing to push.
5622 """
5622 """
5623
5623
5624 opts = pycompat.byteskwargs(opts)
5624 opts = pycompat.byteskwargs(opts)
5625 if opts.get(b'bookmark'):
5625 if opts.get(b'bookmark'):
5626 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5626 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5627 for b in opts[b'bookmark']:
5627 for b in opts[b'bookmark']:
5628 # translate -B options to -r so changesets get pushed
5628 # translate -B options to -r so changesets get pushed
5629 b = repo._bookmarks.expandname(b)
5629 b = repo._bookmarks.expandname(b)
5630 if b in repo._bookmarks:
5630 if b in repo._bookmarks:
5631 opts.setdefault(b'rev', []).append(b)
5631 opts.setdefault(b'rev', []).append(b)
5632 else:
5632 else:
5633 # if we try to push a deleted bookmark, translate it to null
5633 # if we try to push a deleted bookmark, translate it to null
5634 # this lets simultaneous -r, -b options continue working
5634 # this lets simultaneous -r, -b options continue working
5635 opts.setdefault(b'rev', []).append(b"null")
5635 opts.setdefault(b'rev', []).append(b"null")
5636
5636
5637 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5637 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5638 if not path:
5638 if not path:
5639 raise error.Abort(
5639 raise error.Abort(
5640 _(b'default repository not configured!'),
5640 _(b'default repository not configured!'),
5641 hint=_(b"see 'hg help config.paths'"),
5641 hint=_(b"see 'hg help config.paths'"),
5642 )
5642 )
5643 dest = path.pushloc or path.loc
5643 dest = path.pushloc or path.loc
5644 branches = (path.branch, opts.get(b'branch') or [])
5644 branches = (path.branch, opts.get(b'branch') or [])
5645 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5645 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5646 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5646 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5647 other = hg.peer(repo, opts, dest)
5647 other = hg.peer(repo, opts, dest)
5648
5648
5649 if revs:
5649 if revs:
5650 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5650 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5651 if not revs:
5651 if not revs:
5652 raise error.Abort(
5652 raise error.Abort(
5653 _(b"specified revisions evaluate to an empty set"),
5653 _(b"specified revisions evaluate to an empty set"),
5654 hint=_(b"use different revision arguments"),
5654 hint=_(b"use different revision arguments"),
5655 )
5655 )
5656 elif path.pushrev:
5656 elif path.pushrev:
5657 # It doesn't make any sense to specify ancestor revisions. So limit
5657 # It doesn't make any sense to specify ancestor revisions. So limit
5658 # to DAG heads to make discovery simpler.
5658 # to DAG heads to make discovery simpler.
5659 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5659 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5660 revs = scmutil.revrange(repo, [expr])
5660 revs = scmutil.revrange(repo, [expr])
5661 revs = [repo[rev].node() for rev in revs]
5661 revs = [repo[rev].node() for rev in revs]
5662 if not revs:
5662 if not revs:
5663 raise error.Abort(
5663 raise error.Abort(
5664 _(b'default push revset for path evaluates to an empty set')
5664 _(b'default push revset for path evaluates to an empty set')
5665 )
5665 )
5666 elif ui.configbool(b'commands', b'push.require-revs'):
5666 elif ui.configbool(b'commands', b'push.require-revs'):
5667 raise error.Abort(
5667 raise error.Abort(
5668 _(b'no revisions specified to push'),
5668 _(b'no revisions specified to push'),
5669 hint=_(b'did you mean "hg push -r ."?'),
5669 hint=_(b'did you mean "hg push -r ."?'),
5670 )
5670 )
5671
5671
5672 repo._subtoppath = dest
5672 repo._subtoppath = dest
5673 try:
5673 try:
5674 # push subrepos depth-first for coherent ordering
5674 # push subrepos depth-first for coherent ordering
5675 c = repo[b'.']
5675 c = repo[b'.']
5676 subs = c.substate # only repos that are committed
5676 subs = c.substate # only repos that are committed
5677 for s in sorted(subs):
5677 for s in sorted(subs):
5678 result = c.sub(s).push(opts)
5678 result = c.sub(s).push(opts)
5679 if result == 0:
5679 if result == 0:
5680 return not result
5680 return not result
5681 finally:
5681 finally:
5682 del repo._subtoppath
5682 del repo._subtoppath
5683
5683
5684 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5684 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5685 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5685 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5686
5686
5687 pushop = exchange.push(
5687 pushop = exchange.push(
5688 repo,
5688 repo,
5689 other,
5689 other,
5690 opts.get(b'force'),
5690 opts.get(b'force'),
5691 revs=revs,
5691 revs=revs,
5692 newbranch=opts.get(b'new_branch'),
5692 newbranch=opts.get(b'new_branch'),
5693 bookmarks=opts.get(b'bookmark', ()),
5693 bookmarks=opts.get(b'bookmark', ()),
5694 publish=opts.get(b'publish'),
5694 publish=opts.get(b'publish'),
5695 opargs=opargs,
5695 opargs=opargs,
5696 )
5696 )
5697
5697
5698 result = not pushop.cgresult
5698 result = not pushop.cgresult
5699
5699
5700 if pushop.bkresult is not None:
5700 if pushop.bkresult is not None:
5701 if pushop.bkresult == 2:
5701 if pushop.bkresult == 2:
5702 result = 2
5702 result = 2
5703 elif not result and pushop.bkresult:
5703 elif not result and pushop.bkresult:
5704 result = 2
5704 result = 2
5705
5705
5706 return result
5706 return result
5707
5707
5708
5708
5709 @command(
5709 @command(
5710 b'recover',
5710 b'recover',
5711 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5711 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5712 helpcategory=command.CATEGORY_MAINTENANCE,
5712 helpcategory=command.CATEGORY_MAINTENANCE,
5713 )
5713 )
5714 def recover(ui, repo, **opts):
5714 def recover(ui, repo, **opts):
5715 """roll back an interrupted transaction
5715 """roll back an interrupted transaction
5716
5716
5717 Recover from an interrupted commit or pull.
5717 Recover from an interrupted commit or pull.
5718
5718
5719 This command tries to fix the repository status after an
5719 This command tries to fix the repository status after an
5720 interrupted operation. It should only be necessary when Mercurial
5720 interrupted operation. It should only be necessary when Mercurial
5721 suggests it.
5721 suggests it.
5722
5722
5723 Returns 0 if successful, 1 if nothing to recover or verify fails.
5723 Returns 0 if successful, 1 if nothing to recover or verify fails.
5724 """
5724 """
5725 ret = repo.recover()
5725 ret = repo.recover()
5726 if ret:
5726 if ret:
5727 if opts['verify']:
5727 if opts['verify']:
5728 return hg.verify(repo)
5728 return hg.verify(repo)
5729 else:
5729 else:
5730 msg = _(
5730 msg = _(
5731 b"(verify step skipped, run `hg verify` to check your "
5731 b"(verify step skipped, run `hg verify` to check your "
5732 b"repository content)\n"
5732 b"repository content)\n"
5733 )
5733 )
5734 ui.warn(msg)
5734 ui.warn(msg)
5735 return 0
5735 return 0
5736 return 1
5736 return 1
5737
5737
5738
5738
5739 @command(
5739 @command(
5740 b'remove|rm',
5740 b'remove|rm',
5741 [
5741 [
5742 (b'A', b'after', None, _(b'record delete for missing files')),
5742 (b'A', b'after', None, _(b'record delete for missing files')),
5743 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5743 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5744 ]
5744 ]
5745 + subrepoopts
5745 + subrepoopts
5746 + walkopts
5746 + walkopts
5747 + dryrunopts,
5747 + dryrunopts,
5748 _(b'[OPTION]... FILE...'),
5748 _(b'[OPTION]... FILE...'),
5749 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5749 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5750 helpbasic=True,
5750 helpbasic=True,
5751 inferrepo=True,
5751 inferrepo=True,
5752 )
5752 )
5753 def remove(ui, repo, *pats, **opts):
5753 def remove(ui, repo, *pats, **opts):
5754 """remove the specified files on the next commit
5754 """remove the specified files on the next commit
5755
5755
5756 Schedule the indicated files for removal from the current branch.
5756 Schedule the indicated files for removal from the current branch.
5757
5757
5758 This command schedules the files to be removed at the next commit.
5758 This command schedules the files to be removed at the next commit.
5759 To undo a remove before that, see :hg:`revert`. To undo added
5759 To undo a remove before that, see :hg:`revert`. To undo added
5760 files, see :hg:`forget`.
5760 files, see :hg:`forget`.
5761
5761
5762 .. container:: verbose
5762 .. container:: verbose
5763
5763
5764 -A/--after can be used to remove only files that have already
5764 -A/--after can be used to remove only files that have already
5765 been deleted, -f/--force can be used to force deletion, and -Af
5765 been deleted, -f/--force can be used to force deletion, and -Af
5766 can be used to remove files from the next revision without
5766 can be used to remove files from the next revision without
5767 deleting them from the working directory.
5767 deleting them from the working directory.
5768
5768
5769 The following table details the behavior of remove for different
5769 The following table details the behavior of remove for different
5770 file states (columns) and option combinations (rows). The file
5770 file states (columns) and option combinations (rows). The file
5771 states are Added [A], Clean [C], Modified [M] and Missing [!]
5771 states are Added [A], Clean [C], Modified [M] and Missing [!]
5772 (as reported by :hg:`status`). The actions are Warn, Remove
5772 (as reported by :hg:`status`). The actions are Warn, Remove
5773 (from branch) and Delete (from disk):
5773 (from branch) and Delete (from disk):
5774
5774
5775 ========= == == == ==
5775 ========= == == == ==
5776 opt/state A C M !
5776 opt/state A C M !
5777 ========= == == == ==
5777 ========= == == == ==
5778 none W RD W R
5778 none W RD W R
5779 -f R RD RD R
5779 -f R RD RD R
5780 -A W W W R
5780 -A W W W R
5781 -Af R R R R
5781 -Af R R R R
5782 ========= == == == ==
5782 ========= == == == ==
5783
5783
5784 .. note::
5784 .. note::
5785
5785
5786 :hg:`remove` never deletes files in Added [A] state from the
5786 :hg:`remove` never deletes files in Added [A] state from the
5787 working directory, not even if ``--force`` is specified.
5787 working directory, not even if ``--force`` is specified.
5788
5788
5789 Returns 0 on success, 1 if any warnings encountered.
5789 Returns 0 on success, 1 if any warnings encountered.
5790 """
5790 """
5791
5791
5792 opts = pycompat.byteskwargs(opts)
5792 opts = pycompat.byteskwargs(opts)
5793 after, force = opts.get(b'after'), opts.get(b'force')
5793 after, force = opts.get(b'after'), opts.get(b'force')
5794 dryrun = opts.get(b'dry_run')
5794 dryrun = opts.get(b'dry_run')
5795 if not pats and not after:
5795 if not pats and not after:
5796 raise error.Abort(_(b'no files specified'))
5796 raise error.Abort(_(b'no files specified'))
5797
5797
5798 m = scmutil.match(repo[None], pats, opts)
5798 m = scmutil.match(repo[None], pats, opts)
5799 subrepos = opts.get(b'subrepos')
5799 subrepos = opts.get(b'subrepos')
5800 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5800 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5801 return cmdutil.remove(
5801 return cmdutil.remove(
5802 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5802 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5803 )
5803 )
5804
5804
5805
5805
5806 @command(
5806 @command(
5807 b'rename|move|mv',
5807 b'rename|move|mv',
5808 [
5808 [
5809 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5809 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5810 (
5810 (
5811 b'',
5811 b'',
5812 b'at-rev',
5812 b'at-rev',
5813 b'',
5813 b'',
5814 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5814 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5815 _(b'REV'),
5815 _(b'REV'),
5816 ),
5816 ),
5817 (
5817 (
5818 b'f',
5818 b'f',
5819 b'force',
5819 b'force',
5820 None,
5820 None,
5821 _(b'forcibly move over an existing managed file'),
5821 _(b'forcibly move over an existing managed file'),
5822 ),
5822 ),
5823 ]
5823 ]
5824 + walkopts
5824 + walkopts
5825 + dryrunopts,
5825 + dryrunopts,
5826 _(b'[OPTION]... SOURCE... DEST'),
5826 _(b'[OPTION]... SOURCE... DEST'),
5827 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5827 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5828 )
5828 )
5829 def rename(ui, repo, *pats, **opts):
5829 def rename(ui, repo, *pats, **opts):
5830 """rename files; equivalent of copy + remove
5830 """rename files; equivalent of copy + remove
5831
5831
5832 Mark dest as copies of sources; mark sources for deletion. If dest
5832 Mark dest as copies of sources; mark sources for deletion. If dest
5833 is a directory, copies are put in that directory. If dest is a
5833 is a directory, copies are put in that directory. If dest is a
5834 file, there can only be one source.
5834 file, there can only be one source.
5835
5835
5836 By default, this command copies the contents of files as they
5836 By default, this command copies the contents of files as they
5837 exist in the working directory. If invoked with -A/--after, the
5837 exist in the working directory. If invoked with -A/--after, the
5838 operation is recorded, but no copying is performed.
5838 operation is recorded, but no copying is performed.
5839
5839
5840 This command takes effect at the next commit. To undo a rename
5840 This command takes effect at the next commit. To undo a rename
5841 before that, see :hg:`revert`.
5841 before that, see :hg:`revert`.
5842
5842
5843 Returns 0 on success, 1 if errors are encountered.
5843 Returns 0 on success, 1 if errors are encountered.
5844 """
5844 """
5845 opts = pycompat.byteskwargs(opts)
5845 opts = pycompat.byteskwargs(opts)
5846 with repo.wlock():
5846 with repo.wlock():
5847 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5847 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5848
5848
5849
5849
5850 @command(
5850 @command(
5851 b'resolve',
5851 b'resolve',
5852 [
5852 [
5853 (b'a', b'all', None, _(b'select all unresolved files')),
5853 (b'a', b'all', None, _(b'select all unresolved files')),
5854 (b'l', b'list', None, _(b'list state of files needing merge')),
5854 (b'l', b'list', None, _(b'list state of files needing merge')),
5855 (b'm', b'mark', None, _(b'mark files as resolved')),
5855 (b'm', b'mark', None, _(b'mark files as resolved')),
5856 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5856 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5857 (b'n', b'no-status', None, _(b'hide status prefix')),
5857 (b'n', b'no-status', None, _(b'hide status prefix')),
5858 (b'', b're-merge', None, _(b're-merge files')),
5858 (b'', b're-merge', None, _(b're-merge files')),
5859 ]
5859 ]
5860 + mergetoolopts
5860 + mergetoolopts
5861 + walkopts
5861 + walkopts
5862 + formatteropts,
5862 + formatteropts,
5863 _(b'[OPTION]... [FILE]...'),
5863 _(b'[OPTION]... [FILE]...'),
5864 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5864 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5865 inferrepo=True,
5865 inferrepo=True,
5866 )
5866 )
5867 def resolve(ui, repo, *pats, **opts):
5867 def resolve(ui, repo, *pats, **opts):
5868 """redo merges or set/view the merge status of files
5868 """redo merges or set/view the merge status of files
5869
5869
5870 Merges with unresolved conflicts are often the result of
5870 Merges with unresolved conflicts are often the result of
5871 non-interactive merging using the ``internal:merge`` configuration
5871 non-interactive merging using the ``internal:merge`` configuration
5872 setting, or a command-line merge tool like ``diff3``. The resolve
5872 setting, or a command-line merge tool like ``diff3``. The resolve
5873 command is used to manage the files involved in a merge, after
5873 command is used to manage the files involved in a merge, after
5874 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5874 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5875 working directory must have two parents). See :hg:`help
5875 working directory must have two parents). See :hg:`help
5876 merge-tools` for information on configuring merge tools.
5876 merge-tools` for information on configuring merge tools.
5877
5877
5878 The resolve command can be used in the following ways:
5878 The resolve command can be used in the following ways:
5879
5879
5880 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5880 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5881 the specified files, discarding any previous merge attempts. Re-merging
5881 the specified files, discarding any previous merge attempts. Re-merging
5882 is not performed for files already marked as resolved. Use ``--all/-a``
5882 is not performed for files already marked as resolved. Use ``--all/-a``
5883 to select all unresolved files. ``--tool`` can be used to specify
5883 to select all unresolved files. ``--tool`` can be used to specify
5884 the merge tool used for the given files. It overrides the HGMERGE
5884 the merge tool used for the given files. It overrides the HGMERGE
5885 environment variable and your configuration files. Previous file
5885 environment variable and your configuration files. Previous file
5886 contents are saved with a ``.orig`` suffix.
5886 contents are saved with a ``.orig`` suffix.
5887
5887
5888 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5888 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5889 (e.g. after having manually fixed-up the files). The default is
5889 (e.g. after having manually fixed-up the files). The default is
5890 to mark all unresolved files.
5890 to mark all unresolved files.
5891
5891
5892 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5892 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5893 default is to mark all resolved files.
5893 default is to mark all resolved files.
5894
5894
5895 - :hg:`resolve -l`: list files which had or still have conflicts.
5895 - :hg:`resolve -l`: list files which had or still have conflicts.
5896 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5896 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5897 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5897 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5898 the list. See :hg:`help filesets` for details.
5898 the list. See :hg:`help filesets` for details.
5899
5899
5900 .. note::
5900 .. note::
5901
5901
5902 Mercurial will not let you commit files with unresolved merge
5902 Mercurial will not let you commit files with unresolved merge
5903 conflicts. You must use :hg:`resolve -m ...` before you can
5903 conflicts. You must use :hg:`resolve -m ...` before you can
5904 commit after a conflicting merge.
5904 commit after a conflicting merge.
5905
5905
5906 .. container:: verbose
5906 .. container:: verbose
5907
5907
5908 Template:
5908 Template:
5909
5909
5910 The following keywords are supported in addition to the common template
5910 The following keywords are supported in addition to the common template
5911 keywords and functions. See also :hg:`help templates`.
5911 keywords and functions. See also :hg:`help templates`.
5912
5912
5913 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5913 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5914 :path: String. Repository-absolute path of the file.
5914 :path: String. Repository-absolute path of the file.
5915
5915
5916 Returns 0 on success, 1 if any files fail a resolve attempt.
5916 Returns 0 on success, 1 if any files fail a resolve attempt.
5917 """
5917 """
5918
5918
5919 opts = pycompat.byteskwargs(opts)
5919 opts = pycompat.byteskwargs(opts)
5920 confirm = ui.configbool(b'commands', b'resolve.confirm')
5920 confirm = ui.configbool(b'commands', b'resolve.confirm')
5921 flaglist = b'all mark unmark list no_status re_merge'.split()
5921 flaglist = b'all mark unmark list no_status re_merge'.split()
5922 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5922 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5923
5923
5924 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5924 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5925 if actioncount > 1:
5925 if actioncount > 1:
5926 raise error.Abort(_(b"too many actions specified"))
5926 raise error.Abort(_(b"too many actions specified"))
5927 elif actioncount == 0 and ui.configbool(
5927 elif actioncount == 0 and ui.configbool(
5928 b'commands', b'resolve.explicit-re-merge'
5928 b'commands', b'resolve.explicit-re-merge'
5929 ):
5929 ):
5930 hint = _(b'use --mark, --unmark, --list or --re-merge')
5930 hint = _(b'use --mark, --unmark, --list or --re-merge')
5931 raise error.Abort(_(b'no action specified'), hint=hint)
5931 raise error.Abort(_(b'no action specified'), hint=hint)
5932 if pats and all:
5932 if pats and all:
5933 raise error.Abort(_(b"can't specify --all and patterns"))
5933 raise error.Abort(_(b"can't specify --all and patterns"))
5934 if not (all or pats or show or mark or unmark):
5934 if not (all or pats or show or mark or unmark):
5935 raise error.Abort(
5935 raise error.Abort(
5936 _(b'no files or directories specified'),
5936 _(b'no files or directories specified'),
5937 hint=b'use --all to re-merge all unresolved files',
5937 hint=b'use --all to re-merge all unresolved files',
5938 )
5938 )
5939
5939
5940 if confirm:
5940 if confirm:
5941 if all:
5941 if all:
5942 if ui.promptchoice(
5942 if ui.promptchoice(
5943 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5943 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5944 ):
5944 ):
5945 raise error.Abort(_(b'user quit'))
5945 raise error.Abort(_(b'user quit'))
5946 if mark and not pats:
5946 if mark and not pats:
5947 if ui.promptchoice(
5947 if ui.promptchoice(
5948 _(
5948 _(
5949 b'mark all unresolved files as resolved (yn)?'
5949 b'mark all unresolved files as resolved (yn)?'
5950 b'$$ &Yes $$ &No'
5950 b'$$ &Yes $$ &No'
5951 )
5951 )
5952 ):
5952 ):
5953 raise error.Abort(_(b'user quit'))
5953 raise error.Abort(_(b'user quit'))
5954 if unmark and not pats:
5954 if unmark and not pats:
5955 if ui.promptchoice(
5955 if ui.promptchoice(
5956 _(
5956 _(
5957 b'mark all resolved files as unresolved (yn)?'
5957 b'mark all resolved files as unresolved (yn)?'
5958 b'$$ &Yes $$ &No'
5958 b'$$ &Yes $$ &No'
5959 )
5959 )
5960 ):
5960 ):
5961 raise error.Abort(_(b'user quit'))
5961 raise error.Abort(_(b'user quit'))
5962
5962
5963 uipathfn = scmutil.getuipathfn(repo)
5963 uipathfn = scmutil.getuipathfn(repo)
5964
5964
5965 if show:
5965 if show:
5966 ui.pager(b'resolve')
5966 ui.pager(b'resolve')
5967 fm = ui.formatter(b'resolve', opts)
5967 fm = ui.formatter(b'resolve', opts)
5968 ms = mergestatemod.mergestate.read(repo)
5968 ms = mergestatemod.mergestate.read(repo)
5969 wctx = repo[None]
5969 wctx = repo[None]
5970 m = scmutil.match(wctx, pats, opts)
5970 m = scmutil.match(wctx, pats, opts)
5971
5971
5972 # Labels and keys based on merge state. Unresolved path conflicts show
5972 # Labels and keys based on merge state. Unresolved path conflicts show
5973 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5973 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5974 # resolved conflicts.
5974 # resolved conflicts.
5975 mergestateinfo = {
5975 mergestateinfo = {
5976 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5976 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5977 b'resolve.unresolved',
5977 b'resolve.unresolved',
5978 b'U',
5978 b'U',
5979 ),
5979 ),
5980 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5980 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5981 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5981 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5982 b'resolve.unresolved',
5982 b'resolve.unresolved',
5983 b'P',
5983 b'P',
5984 ),
5984 ),
5985 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5985 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5986 b'resolve.resolved',
5986 b'resolve.resolved',
5987 b'R',
5987 b'R',
5988 ),
5988 ),
5989 mergestatemod.MERGE_RECORD_DRIVER_RESOLVED: (
5990 b'resolve.driverresolved',
5991 b'D',
5992 ),
5993 }
5989 }
5994
5990
5995 for f in ms:
5991 for f in ms:
5996 if not m(f):
5992 if not m(f):
5997 continue
5993 continue
5998
5994
5999 label, key = mergestateinfo[ms[f]]
5995 label, key = mergestateinfo[ms[f]]
6000 fm.startitem()
5996 fm.startitem()
6001 fm.context(ctx=wctx)
5997 fm.context(ctx=wctx)
6002 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5998 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6003 fm.data(path=f)
5999 fm.data(path=f)
6004 fm.plain(b'%s\n' % uipathfn(f), label=label)
6000 fm.plain(b'%s\n' % uipathfn(f), label=label)
6005 fm.end()
6001 fm.end()
6006 return 0
6002 return 0
6007
6003
6008 with repo.wlock():
6004 with repo.wlock():
6009 ms = mergestatemod.mergestate.read(repo)
6005 ms = mergestatemod.mergestate.read(repo)
6010
6006
6011 if not (ms.active() or repo.dirstate.p2() != nullid):
6007 if not (ms.active() or repo.dirstate.p2() != nullid):
6012 raise error.Abort(
6008 raise error.Abort(
6013 _(b'resolve command not applicable when not merging')
6009 _(b'resolve command not applicable when not merging')
6014 )
6010 )
6015
6011
6016 wctx = repo[None]
6012 wctx = repo[None]
6017
6018 if (
6019 ms.mergedriver
6020 and ms.mdstate() == mergestatemod.MERGE_DRIVER_STATE_UNMARKED
6021 ):
6022 proceed = mergemod.driverpreprocess(repo, ms, wctx)
6023 ms.commit()
6024 # allow mark and unmark to go through
6025 if not mark and not unmark and not proceed:
6026 return 1
6027
6028 m = scmutil.match(wctx, pats, opts)
6013 m = scmutil.match(wctx, pats, opts)
6029 ret = 0
6014 ret = 0
6030 didwork = False
6015 didwork = False
6031 runconclude = False
6032
6016
6033 tocomplete = []
6017 tocomplete = []
6034 hasconflictmarkers = []
6018 hasconflictmarkers = []
6035 if mark:
6019 if mark:
6036 markcheck = ui.config(b'commands', b'resolve.mark-check')
6020 markcheck = ui.config(b'commands', b'resolve.mark-check')
6037 if markcheck not in [b'warn', b'abort']:
6021 if markcheck not in [b'warn', b'abort']:
6038 # Treat all invalid / unrecognized values as 'none'.
6022 # Treat all invalid / unrecognized values as 'none'.
6039 markcheck = False
6023 markcheck = False
6040 for f in ms:
6024 for f in ms:
6041 if not m(f):
6025 if not m(f):
6042 continue
6026 continue
6043
6027
6044 didwork = True
6028 didwork = True
6045
6029
6046 # don't let driver-resolved files be marked, and run the conclude
6047 # step if asked to resolve
6048 if ms[f] == mergestatemod.MERGE_RECORD_DRIVER_RESOLVED:
6049 exact = m.exact(f)
6050 if mark:
6051 if exact:
6052 ui.warn(
6053 _(b'not marking %s as it is driver-resolved\n')
6054 % uipathfn(f)
6055 )
6056 elif unmark:
6057 if exact:
6058 ui.warn(
6059 _(b'not unmarking %s as it is driver-resolved\n')
6060 % uipathfn(f)
6061 )
6062 else:
6063 runconclude = True
6064 continue
6065
6066 # path conflicts must be resolved manually
6030 # path conflicts must be resolved manually
6067 if ms[f] in (
6031 if ms[f] in (
6068 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6032 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6069 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6033 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6070 ):
6034 ):
6071 if mark:
6035 if mark:
6072 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6036 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6073 elif unmark:
6037 elif unmark:
6074 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6038 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6075 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6039 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6076 ui.warn(
6040 ui.warn(
6077 _(b'%s: path conflict must be resolved manually\n')
6041 _(b'%s: path conflict must be resolved manually\n')
6078 % uipathfn(f)
6042 % uipathfn(f)
6079 )
6043 )
6080 continue
6044 continue
6081
6045
6082 if mark:
6046 if mark:
6083 if markcheck:
6047 if markcheck:
6084 fdata = repo.wvfs.tryread(f)
6048 fdata = repo.wvfs.tryread(f)
6085 if (
6049 if (
6086 filemerge.hasconflictmarkers(fdata)
6050 filemerge.hasconflictmarkers(fdata)
6087 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6051 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6088 ):
6052 ):
6089 hasconflictmarkers.append(f)
6053 hasconflictmarkers.append(f)
6090 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6054 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6091 elif unmark:
6055 elif unmark:
6092 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6056 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6093 else:
6057 else:
6094 # backup pre-resolve (merge uses .orig for its own purposes)
6058 # backup pre-resolve (merge uses .orig for its own purposes)
6095 a = repo.wjoin(f)
6059 a = repo.wjoin(f)
6096 try:
6060 try:
6097 util.copyfile(a, a + b".resolve")
6061 util.copyfile(a, a + b".resolve")
6098 except (IOError, OSError) as inst:
6062 except (IOError, OSError) as inst:
6099 if inst.errno != errno.ENOENT:
6063 if inst.errno != errno.ENOENT:
6100 raise
6064 raise
6101
6065
6102 try:
6066 try:
6103 # preresolve file
6067 # preresolve file
6104 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6068 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6105 with ui.configoverride(overrides, b'resolve'):
6069 with ui.configoverride(overrides, b'resolve'):
6106 complete, r = ms.preresolve(f, wctx)
6070 complete, r = ms.preresolve(f, wctx)
6107 if not complete:
6071 if not complete:
6108 tocomplete.append(f)
6072 tocomplete.append(f)
6109 elif r:
6073 elif r:
6110 ret = 1
6074 ret = 1
6111 finally:
6075 finally:
6112 ms.commit()
6076 ms.commit()
6113
6077
6114 # replace filemerge's .orig file with our resolve file, but only
6078 # replace filemerge's .orig file with our resolve file, but only
6115 # for merges that are complete
6079 # for merges that are complete
6116 if complete:
6080 if complete:
6117 try:
6081 try:
6118 util.rename(
6082 util.rename(
6119 a + b".resolve", scmutil.backuppath(ui, repo, f)
6083 a + b".resolve", scmutil.backuppath(ui, repo, f)
6120 )
6084 )
6121 except OSError as inst:
6085 except OSError as inst:
6122 if inst.errno != errno.ENOENT:
6086 if inst.errno != errno.ENOENT:
6123 raise
6087 raise
6124
6088
6125 if hasconflictmarkers:
6089 if hasconflictmarkers:
6126 ui.warn(
6090 ui.warn(
6127 _(
6091 _(
6128 b'warning: the following files still have conflict '
6092 b'warning: the following files still have conflict '
6129 b'markers:\n'
6093 b'markers:\n'
6130 )
6094 )
6131 + b''.join(
6095 + b''.join(
6132 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6096 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6133 )
6097 )
6134 )
6098 )
6135 if markcheck == b'abort' and not all and not pats:
6099 if markcheck == b'abort' and not all and not pats:
6136 raise error.Abort(
6100 raise error.Abort(
6137 _(b'conflict markers detected'),
6101 _(b'conflict markers detected'),
6138 hint=_(b'use --all to mark anyway'),
6102 hint=_(b'use --all to mark anyway'),
6139 )
6103 )
6140
6104
6141 for f in tocomplete:
6105 for f in tocomplete:
6142 try:
6106 try:
6143 # resolve file
6107 # resolve file
6144 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6108 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6145 with ui.configoverride(overrides, b'resolve'):
6109 with ui.configoverride(overrides, b'resolve'):
6146 r = ms.resolve(f, wctx)
6110 r = ms.resolve(f, wctx)
6147 if r:
6111 if r:
6148 ret = 1
6112 ret = 1
6149 finally:
6113 finally:
6150 ms.commit()
6114 ms.commit()
6151
6115
6152 # replace filemerge's .orig file with our resolve file
6116 # replace filemerge's .orig file with our resolve file
6153 a = repo.wjoin(f)
6117 a = repo.wjoin(f)
6154 try:
6118 try:
6155 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6119 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6156 except OSError as inst:
6120 except OSError as inst:
6157 if inst.errno != errno.ENOENT:
6121 if inst.errno != errno.ENOENT:
6158 raise
6122 raise
6159
6123
6160 ms.commit()
6124 ms.commit()
6161 branchmerge = repo.dirstate.p2() != nullid
6125 branchmerge = repo.dirstate.p2() != nullid
6162 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6126 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6163
6127
6164 if not didwork and pats:
6128 if not didwork and pats:
6165 hint = None
6129 hint = None
6166 if not any([p for p in pats if p.find(b':') >= 0]):
6130 if not any([p for p in pats if p.find(b':') >= 0]):
6167 pats = [b'path:%s' % p for p in pats]
6131 pats = [b'path:%s' % p for p in pats]
6168 m = scmutil.match(wctx, pats, opts)
6132 m = scmutil.match(wctx, pats, opts)
6169 for f in ms:
6133 for f in ms:
6170 if not m(f):
6134 if not m(f):
6171 continue
6135 continue
6172
6136
6173 def flag(o):
6137 def flag(o):
6174 if o == b're_merge':
6138 if o == b're_merge':
6175 return b'--re-merge '
6139 return b'--re-merge '
6176 return b'-%s ' % o[0:1]
6140 return b'-%s ' % o[0:1]
6177
6141
6178 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6142 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6179 hint = _(b"(try: hg resolve %s%s)\n") % (
6143 hint = _(b"(try: hg resolve %s%s)\n") % (
6180 flags,
6144 flags,
6181 b' '.join(pats),
6145 b' '.join(pats),
6182 )
6146 )
6183 break
6147 break
6184 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6148 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6185 if hint:
6149 if hint:
6186 ui.warn(hint)
6150 ui.warn(hint)
6187 elif ms.mergedriver and ms.mdstate() != b's':
6151
6188 # run conclude step when either a driver-resolved file is requested
6189 # or there are no driver-resolved files
6190 # we can't use 'ret' to determine whether any files are unresolved
6191 # because we might not have tried to resolve some
6192 if (runconclude or not list(ms.driverresolved())) and not list(
6193 ms.unresolved()
6194 ):
6195 proceed = mergemod.driverconclude(repo, ms, wctx)
6196 ms.commit()
6197 if not proceed:
6198 return 1
6199
6200 # Nudge users into finishing an unfinished operation
6201 unresolvedf = list(ms.unresolved())
6152 unresolvedf = list(ms.unresolved())
6202 driverresolvedf = list(ms.driverresolved())
6153 if not unresolvedf:
6203 if not unresolvedf and not driverresolvedf:
6204 ui.status(_(b'(no more unresolved files)\n'))
6154 ui.status(_(b'(no more unresolved files)\n'))
6205 cmdutil.checkafterresolved(repo)
6155 cmdutil.checkafterresolved(repo)
6206 elif not unresolvedf:
6207 ui.status(
6208 _(
6209 b'(no more unresolved files -- '
6210 b'run "hg resolve --all" to conclude)\n'
6211 )
6212 )
6213
6156
6214 return ret
6157 return ret
6215
6158
6216
6159
6217 @command(
6160 @command(
6218 b'revert',
6161 b'revert',
6219 [
6162 [
6220 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6163 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6221 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6164 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6222 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6165 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6223 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6166 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6224 (b'i', b'interactive', None, _(b'interactively select the changes')),
6167 (b'i', b'interactive', None, _(b'interactively select the changes')),
6225 ]
6168 ]
6226 + walkopts
6169 + walkopts
6227 + dryrunopts,
6170 + dryrunopts,
6228 _(b'[OPTION]... [-r REV] [NAME]...'),
6171 _(b'[OPTION]... [-r REV] [NAME]...'),
6229 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6172 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6230 )
6173 )
6231 def revert(ui, repo, *pats, **opts):
6174 def revert(ui, repo, *pats, **opts):
6232 """restore files to their checkout state
6175 """restore files to their checkout state
6233
6176
6234 .. note::
6177 .. note::
6235
6178
6236 To check out earlier revisions, you should use :hg:`update REV`.
6179 To check out earlier revisions, you should use :hg:`update REV`.
6237 To cancel an uncommitted merge (and lose your changes),
6180 To cancel an uncommitted merge (and lose your changes),
6238 use :hg:`merge --abort`.
6181 use :hg:`merge --abort`.
6239
6182
6240 With no revision specified, revert the specified files or directories
6183 With no revision specified, revert the specified files or directories
6241 to the contents they had in the parent of the working directory.
6184 to the contents they had in the parent of the working directory.
6242 This restores the contents of files to an unmodified
6185 This restores the contents of files to an unmodified
6243 state and unschedules adds, removes, copies, and renames. If the
6186 state and unschedules adds, removes, copies, and renames. If the
6244 working directory has two parents, you must explicitly specify a
6187 working directory has two parents, you must explicitly specify a
6245 revision.
6188 revision.
6246
6189
6247 Using the -r/--rev or -d/--date options, revert the given files or
6190 Using the -r/--rev or -d/--date options, revert the given files or
6248 directories to their states as of a specific revision. Because
6191 directories to their states as of a specific revision. Because
6249 revert does not change the working directory parents, this will
6192 revert does not change the working directory parents, this will
6250 cause these files to appear modified. This can be helpful to "back
6193 cause these files to appear modified. This can be helpful to "back
6251 out" some or all of an earlier change. See :hg:`backout` for a
6194 out" some or all of an earlier change. See :hg:`backout` for a
6252 related method.
6195 related method.
6253
6196
6254 Modified files are saved with a .orig suffix before reverting.
6197 Modified files are saved with a .orig suffix before reverting.
6255 To disable these backups, use --no-backup. It is possible to store
6198 To disable these backups, use --no-backup. It is possible to store
6256 the backup files in a custom directory relative to the root of the
6199 the backup files in a custom directory relative to the root of the
6257 repository by setting the ``ui.origbackuppath`` configuration
6200 repository by setting the ``ui.origbackuppath`` configuration
6258 option.
6201 option.
6259
6202
6260 See :hg:`help dates` for a list of formats valid for -d/--date.
6203 See :hg:`help dates` for a list of formats valid for -d/--date.
6261
6204
6262 See :hg:`help backout` for a way to reverse the effect of an
6205 See :hg:`help backout` for a way to reverse the effect of an
6263 earlier changeset.
6206 earlier changeset.
6264
6207
6265 Returns 0 on success.
6208 Returns 0 on success.
6266 """
6209 """
6267
6210
6268 opts = pycompat.byteskwargs(opts)
6211 opts = pycompat.byteskwargs(opts)
6269 if opts.get(b"date"):
6212 if opts.get(b"date"):
6270 if opts.get(b"rev"):
6213 if opts.get(b"rev"):
6271 raise error.Abort(_(b"you can't specify a revision and a date"))
6214 raise error.Abort(_(b"you can't specify a revision and a date"))
6272 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6215 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6273
6216
6274 parent, p2 = repo.dirstate.parents()
6217 parent, p2 = repo.dirstate.parents()
6275 if not opts.get(b'rev') and p2 != nullid:
6218 if not opts.get(b'rev') and p2 != nullid:
6276 # revert after merge is a trap for new users (issue2915)
6219 # revert after merge is a trap for new users (issue2915)
6277 raise error.Abort(
6220 raise error.Abort(
6278 _(b'uncommitted merge with no revision specified'),
6221 _(b'uncommitted merge with no revision specified'),
6279 hint=_(b"use 'hg update' or see 'hg help revert'"),
6222 hint=_(b"use 'hg update' or see 'hg help revert'"),
6280 )
6223 )
6281
6224
6282 rev = opts.get(b'rev')
6225 rev = opts.get(b'rev')
6283 if rev:
6226 if rev:
6284 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6227 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6285 ctx = scmutil.revsingle(repo, rev)
6228 ctx = scmutil.revsingle(repo, rev)
6286
6229
6287 if not (
6230 if not (
6288 pats
6231 pats
6289 or opts.get(b'include')
6232 or opts.get(b'include')
6290 or opts.get(b'exclude')
6233 or opts.get(b'exclude')
6291 or opts.get(b'all')
6234 or opts.get(b'all')
6292 or opts.get(b'interactive')
6235 or opts.get(b'interactive')
6293 ):
6236 ):
6294 msg = _(b"no files or directories specified")
6237 msg = _(b"no files or directories specified")
6295 if p2 != nullid:
6238 if p2 != nullid:
6296 hint = _(
6239 hint = _(
6297 b"uncommitted merge, use --all to discard all changes,"
6240 b"uncommitted merge, use --all to discard all changes,"
6298 b" or 'hg update -C .' to abort the merge"
6241 b" or 'hg update -C .' to abort the merge"
6299 )
6242 )
6300 raise error.Abort(msg, hint=hint)
6243 raise error.Abort(msg, hint=hint)
6301 dirty = any(repo.status())
6244 dirty = any(repo.status())
6302 node = ctx.node()
6245 node = ctx.node()
6303 if node != parent:
6246 if node != parent:
6304 if dirty:
6247 if dirty:
6305 hint = (
6248 hint = (
6306 _(
6249 _(
6307 b"uncommitted changes, use --all to discard all"
6250 b"uncommitted changes, use --all to discard all"
6308 b" changes, or 'hg update %d' to update"
6251 b" changes, or 'hg update %d' to update"
6309 )
6252 )
6310 % ctx.rev()
6253 % ctx.rev()
6311 )
6254 )
6312 else:
6255 else:
6313 hint = (
6256 hint = (
6314 _(
6257 _(
6315 b"use --all to revert all files,"
6258 b"use --all to revert all files,"
6316 b" or 'hg update %d' to update"
6259 b" or 'hg update %d' to update"
6317 )
6260 )
6318 % ctx.rev()
6261 % ctx.rev()
6319 )
6262 )
6320 elif dirty:
6263 elif dirty:
6321 hint = _(b"uncommitted changes, use --all to discard all changes")
6264 hint = _(b"uncommitted changes, use --all to discard all changes")
6322 else:
6265 else:
6323 hint = _(b"use --all to revert all files")
6266 hint = _(b"use --all to revert all files")
6324 raise error.Abort(msg, hint=hint)
6267 raise error.Abort(msg, hint=hint)
6325
6268
6326 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6269 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6327
6270
6328
6271
6329 @command(
6272 @command(
6330 b'rollback',
6273 b'rollback',
6331 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6274 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6332 helpcategory=command.CATEGORY_MAINTENANCE,
6275 helpcategory=command.CATEGORY_MAINTENANCE,
6333 )
6276 )
6334 def rollback(ui, repo, **opts):
6277 def rollback(ui, repo, **opts):
6335 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6278 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6336
6279
6337 Please use :hg:`commit --amend` instead of rollback to correct
6280 Please use :hg:`commit --amend` instead of rollback to correct
6338 mistakes in the last commit.
6281 mistakes in the last commit.
6339
6282
6340 This command should be used with care. There is only one level of
6283 This command should be used with care. There is only one level of
6341 rollback, and there is no way to undo a rollback. It will also
6284 rollback, and there is no way to undo a rollback. It will also
6342 restore the dirstate at the time of the last transaction, losing
6285 restore the dirstate at the time of the last transaction, losing
6343 any dirstate changes since that time. This command does not alter
6286 any dirstate changes since that time. This command does not alter
6344 the working directory.
6287 the working directory.
6345
6288
6346 Transactions are used to encapsulate the effects of all commands
6289 Transactions are used to encapsulate the effects of all commands
6347 that create new changesets or propagate existing changesets into a
6290 that create new changesets or propagate existing changesets into a
6348 repository.
6291 repository.
6349
6292
6350 .. container:: verbose
6293 .. container:: verbose
6351
6294
6352 For example, the following commands are transactional, and their
6295 For example, the following commands are transactional, and their
6353 effects can be rolled back:
6296 effects can be rolled back:
6354
6297
6355 - commit
6298 - commit
6356 - import
6299 - import
6357 - pull
6300 - pull
6358 - push (with this repository as the destination)
6301 - push (with this repository as the destination)
6359 - unbundle
6302 - unbundle
6360
6303
6361 To avoid permanent data loss, rollback will refuse to rollback a
6304 To avoid permanent data loss, rollback will refuse to rollback a
6362 commit transaction if it isn't checked out. Use --force to
6305 commit transaction if it isn't checked out. Use --force to
6363 override this protection.
6306 override this protection.
6364
6307
6365 The rollback command can be entirely disabled by setting the
6308 The rollback command can be entirely disabled by setting the
6366 ``ui.rollback`` configuration setting to false. If you're here
6309 ``ui.rollback`` configuration setting to false. If you're here
6367 because you want to use rollback and it's disabled, you can
6310 because you want to use rollback and it's disabled, you can
6368 re-enable the command by setting ``ui.rollback`` to true.
6311 re-enable the command by setting ``ui.rollback`` to true.
6369
6312
6370 This command is not intended for use on public repositories. Once
6313 This command is not intended for use on public repositories. Once
6371 changes are visible for pull by other users, rolling a transaction
6314 changes are visible for pull by other users, rolling a transaction
6372 back locally is ineffective (someone else may already have pulled
6315 back locally is ineffective (someone else may already have pulled
6373 the changes). Furthermore, a race is possible with readers of the
6316 the changes). Furthermore, a race is possible with readers of the
6374 repository; for example an in-progress pull from the repository
6317 repository; for example an in-progress pull from the repository
6375 may fail if a rollback is performed.
6318 may fail if a rollback is performed.
6376
6319
6377 Returns 0 on success, 1 if no rollback data is available.
6320 Returns 0 on success, 1 if no rollback data is available.
6378 """
6321 """
6379 if not ui.configbool(b'ui', b'rollback'):
6322 if not ui.configbool(b'ui', b'rollback'):
6380 raise error.Abort(
6323 raise error.Abort(
6381 _(b'rollback is disabled because it is unsafe'),
6324 _(b'rollback is disabled because it is unsafe'),
6382 hint=b'see `hg help -v rollback` for information',
6325 hint=b'see `hg help -v rollback` for information',
6383 )
6326 )
6384 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6327 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6385
6328
6386
6329
6387 @command(
6330 @command(
6388 b'root',
6331 b'root',
6389 [] + formatteropts,
6332 [] + formatteropts,
6390 intents={INTENT_READONLY},
6333 intents={INTENT_READONLY},
6391 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6334 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6392 )
6335 )
6393 def root(ui, repo, **opts):
6336 def root(ui, repo, **opts):
6394 """print the root (top) of the current working directory
6337 """print the root (top) of the current working directory
6395
6338
6396 Print the root directory of the current repository.
6339 Print the root directory of the current repository.
6397
6340
6398 .. container:: verbose
6341 .. container:: verbose
6399
6342
6400 Template:
6343 Template:
6401
6344
6402 The following keywords are supported in addition to the common template
6345 The following keywords are supported in addition to the common template
6403 keywords and functions. See also :hg:`help templates`.
6346 keywords and functions. See also :hg:`help templates`.
6404
6347
6405 :hgpath: String. Path to the .hg directory.
6348 :hgpath: String. Path to the .hg directory.
6406 :storepath: String. Path to the directory holding versioned data.
6349 :storepath: String. Path to the directory holding versioned data.
6407
6350
6408 Returns 0 on success.
6351 Returns 0 on success.
6409 """
6352 """
6410 opts = pycompat.byteskwargs(opts)
6353 opts = pycompat.byteskwargs(opts)
6411 with ui.formatter(b'root', opts) as fm:
6354 with ui.formatter(b'root', opts) as fm:
6412 fm.startitem()
6355 fm.startitem()
6413 fm.write(b'reporoot', b'%s\n', repo.root)
6356 fm.write(b'reporoot', b'%s\n', repo.root)
6414 fm.data(hgpath=repo.path, storepath=repo.spath)
6357 fm.data(hgpath=repo.path, storepath=repo.spath)
6415
6358
6416
6359
6417 @command(
6360 @command(
6418 b'serve',
6361 b'serve',
6419 [
6362 [
6420 (
6363 (
6421 b'A',
6364 b'A',
6422 b'accesslog',
6365 b'accesslog',
6423 b'',
6366 b'',
6424 _(b'name of access log file to write to'),
6367 _(b'name of access log file to write to'),
6425 _(b'FILE'),
6368 _(b'FILE'),
6426 ),
6369 ),
6427 (b'd', b'daemon', None, _(b'run server in background')),
6370 (b'd', b'daemon', None, _(b'run server in background')),
6428 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6371 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6429 (
6372 (
6430 b'E',
6373 b'E',
6431 b'errorlog',
6374 b'errorlog',
6432 b'',
6375 b'',
6433 _(b'name of error log file to write to'),
6376 _(b'name of error log file to write to'),
6434 _(b'FILE'),
6377 _(b'FILE'),
6435 ),
6378 ),
6436 # use string type, then we can check if something was passed
6379 # use string type, then we can check if something was passed
6437 (
6380 (
6438 b'p',
6381 b'p',
6439 b'port',
6382 b'port',
6440 b'',
6383 b'',
6441 _(b'port to listen on (default: 8000)'),
6384 _(b'port to listen on (default: 8000)'),
6442 _(b'PORT'),
6385 _(b'PORT'),
6443 ),
6386 ),
6444 (
6387 (
6445 b'a',
6388 b'a',
6446 b'address',
6389 b'address',
6447 b'',
6390 b'',
6448 _(b'address to listen on (default: all interfaces)'),
6391 _(b'address to listen on (default: all interfaces)'),
6449 _(b'ADDR'),
6392 _(b'ADDR'),
6450 ),
6393 ),
6451 (
6394 (
6452 b'',
6395 b'',
6453 b'prefix',
6396 b'prefix',
6454 b'',
6397 b'',
6455 _(b'prefix path to serve from (default: server root)'),
6398 _(b'prefix path to serve from (default: server root)'),
6456 _(b'PREFIX'),
6399 _(b'PREFIX'),
6457 ),
6400 ),
6458 (
6401 (
6459 b'n',
6402 b'n',
6460 b'name',
6403 b'name',
6461 b'',
6404 b'',
6462 _(b'name to show in web pages (default: working directory)'),
6405 _(b'name to show in web pages (default: working directory)'),
6463 _(b'NAME'),
6406 _(b'NAME'),
6464 ),
6407 ),
6465 (
6408 (
6466 b'',
6409 b'',
6467 b'web-conf',
6410 b'web-conf',
6468 b'',
6411 b'',
6469 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6412 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6470 _(b'FILE'),
6413 _(b'FILE'),
6471 ),
6414 ),
6472 (
6415 (
6473 b'',
6416 b'',
6474 b'webdir-conf',
6417 b'webdir-conf',
6475 b'',
6418 b'',
6476 _(b'name of the hgweb config file (DEPRECATED)'),
6419 _(b'name of the hgweb config file (DEPRECATED)'),
6477 _(b'FILE'),
6420 _(b'FILE'),
6478 ),
6421 ),
6479 (
6422 (
6480 b'',
6423 b'',
6481 b'pid-file',
6424 b'pid-file',
6482 b'',
6425 b'',
6483 _(b'name of file to write process ID to'),
6426 _(b'name of file to write process ID to'),
6484 _(b'FILE'),
6427 _(b'FILE'),
6485 ),
6428 ),
6486 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6429 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6487 (
6430 (
6488 b'',
6431 b'',
6489 b'cmdserver',
6432 b'cmdserver',
6490 b'',
6433 b'',
6491 _(b'for remote clients (ADVANCED)'),
6434 _(b'for remote clients (ADVANCED)'),
6492 _(b'MODE'),
6435 _(b'MODE'),
6493 ),
6436 ),
6494 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6437 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6495 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6438 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6496 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6439 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6497 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6440 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6498 (b'', b'print-url', None, _(b'start and print only the URL')),
6441 (b'', b'print-url', None, _(b'start and print only the URL')),
6499 ]
6442 ]
6500 + subrepoopts,
6443 + subrepoopts,
6501 _(b'[OPTION]...'),
6444 _(b'[OPTION]...'),
6502 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6445 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6503 helpbasic=True,
6446 helpbasic=True,
6504 optionalrepo=True,
6447 optionalrepo=True,
6505 )
6448 )
6506 def serve(ui, repo, **opts):
6449 def serve(ui, repo, **opts):
6507 """start stand-alone webserver
6450 """start stand-alone webserver
6508
6451
6509 Start a local HTTP repository browser and pull server. You can use
6452 Start a local HTTP repository browser and pull server. You can use
6510 this for ad-hoc sharing and browsing of repositories. It is
6453 this for ad-hoc sharing and browsing of repositories. It is
6511 recommended to use a real web server to serve a repository for
6454 recommended to use a real web server to serve a repository for
6512 longer periods of time.
6455 longer periods of time.
6513
6456
6514 Please note that the server does not implement access control.
6457 Please note that the server does not implement access control.
6515 This means that, by default, anybody can read from the server and
6458 This means that, by default, anybody can read from the server and
6516 nobody can write to it by default. Set the ``web.allow-push``
6459 nobody can write to it by default. Set the ``web.allow-push``
6517 option to ``*`` to allow everybody to push to the server. You
6460 option to ``*`` to allow everybody to push to the server. You
6518 should use a real web server if you need to authenticate users.
6461 should use a real web server if you need to authenticate users.
6519
6462
6520 By default, the server logs accesses to stdout and errors to
6463 By default, the server logs accesses to stdout and errors to
6521 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6464 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6522 files.
6465 files.
6523
6466
6524 To have the server choose a free port number to listen on, specify
6467 To have the server choose a free port number to listen on, specify
6525 a port number of 0; in this case, the server will print the port
6468 a port number of 0; in this case, the server will print the port
6526 number it uses.
6469 number it uses.
6527
6470
6528 Returns 0 on success.
6471 Returns 0 on success.
6529 """
6472 """
6530
6473
6531 opts = pycompat.byteskwargs(opts)
6474 opts = pycompat.byteskwargs(opts)
6532 if opts[b"stdio"] and opts[b"cmdserver"]:
6475 if opts[b"stdio"] and opts[b"cmdserver"]:
6533 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6476 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6534 if opts[b"print_url"] and ui.verbose:
6477 if opts[b"print_url"] and ui.verbose:
6535 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6478 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6536
6479
6537 if opts[b"stdio"]:
6480 if opts[b"stdio"]:
6538 if repo is None:
6481 if repo is None:
6539 raise error.RepoError(
6482 raise error.RepoError(
6540 _(b"there is no Mercurial repository here (.hg not found)")
6483 _(b"there is no Mercurial repository here (.hg not found)")
6541 )
6484 )
6542 s = wireprotoserver.sshserver(ui, repo)
6485 s = wireprotoserver.sshserver(ui, repo)
6543 s.serve_forever()
6486 s.serve_forever()
6544
6487
6545 service = server.createservice(ui, repo, opts)
6488 service = server.createservice(ui, repo, opts)
6546 return server.runservice(opts, initfn=service.init, runfn=service.run)
6489 return server.runservice(opts, initfn=service.init, runfn=service.run)
6547
6490
6548
6491
6549 @command(
6492 @command(
6550 b'shelve',
6493 b'shelve',
6551 [
6494 [
6552 (
6495 (
6553 b'A',
6496 b'A',
6554 b'addremove',
6497 b'addremove',
6555 None,
6498 None,
6556 _(b'mark new/missing files as added/removed before shelving'),
6499 _(b'mark new/missing files as added/removed before shelving'),
6557 ),
6500 ),
6558 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6501 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6559 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6502 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6560 (
6503 (
6561 b'',
6504 b'',
6562 b'date',
6505 b'date',
6563 b'',
6506 b'',
6564 _(b'shelve with the specified commit date'),
6507 _(b'shelve with the specified commit date'),
6565 _(b'DATE'),
6508 _(b'DATE'),
6566 ),
6509 ),
6567 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6510 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6568 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6511 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6569 (
6512 (
6570 b'k',
6513 b'k',
6571 b'keep',
6514 b'keep',
6572 False,
6515 False,
6573 _(b'shelve, but keep changes in the working directory'),
6516 _(b'shelve, but keep changes in the working directory'),
6574 ),
6517 ),
6575 (b'l', b'list', None, _(b'list current shelves')),
6518 (b'l', b'list', None, _(b'list current shelves')),
6576 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6519 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6577 (
6520 (
6578 b'n',
6521 b'n',
6579 b'name',
6522 b'name',
6580 b'',
6523 b'',
6581 _(b'use the given name for the shelved commit'),
6524 _(b'use the given name for the shelved commit'),
6582 _(b'NAME'),
6525 _(b'NAME'),
6583 ),
6526 ),
6584 (
6527 (
6585 b'p',
6528 b'p',
6586 b'patch',
6529 b'patch',
6587 None,
6530 None,
6588 _(
6531 _(
6589 b'output patches for changes (provide the names of the shelved '
6532 b'output patches for changes (provide the names of the shelved '
6590 b'changes as positional arguments)'
6533 b'changes as positional arguments)'
6591 ),
6534 ),
6592 ),
6535 ),
6593 (b'i', b'interactive', None, _(b'interactive mode')),
6536 (b'i', b'interactive', None, _(b'interactive mode')),
6594 (
6537 (
6595 b'',
6538 b'',
6596 b'stat',
6539 b'stat',
6597 None,
6540 None,
6598 _(
6541 _(
6599 b'output diffstat-style summary of changes (provide the names of '
6542 b'output diffstat-style summary of changes (provide the names of '
6600 b'the shelved changes as positional arguments)'
6543 b'the shelved changes as positional arguments)'
6601 ),
6544 ),
6602 ),
6545 ),
6603 ]
6546 ]
6604 + cmdutil.walkopts,
6547 + cmdutil.walkopts,
6605 _(b'hg shelve [OPTION]... [FILE]...'),
6548 _(b'hg shelve [OPTION]... [FILE]...'),
6606 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6549 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6607 )
6550 )
6608 def shelve(ui, repo, *pats, **opts):
6551 def shelve(ui, repo, *pats, **opts):
6609 '''save and set aside changes from the working directory
6552 '''save and set aside changes from the working directory
6610
6553
6611 Shelving takes files that "hg status" reports as not clean, saves
6554 Shelving takes files that "hg status" reports as not clean, saves
6612 the modifications to a bundle (a shelved change), and reverts the
6555 the modifications to a bundle (a shelved change), and reverts the
6613 files so that their state in the working directory becomes clean.
6556 files so that their state in the working directory becomes clean.
6614
6557
6615 To restore these changes to the working directory, using "hg
6558 To restore these changes to the working directory, using "hg
6616 unshelve"; this will work even if you switch to a different
6559 unshelve"; this will work even if you switch to a different
6617 commit.
6560 commit.
6618
6561
6619 When no files are specified, "hg shelve" saves all not-clean
6562 When no files are specified, "hg shelve" saves all not-clean
6620 files. If specific files or directories are named, only changes to
6563 files. If specific files or directories are named, only changes to
6621 those files are shelved.
6564 those files are shelved.
6622
6565
6623 In bare shelve (when no files are specified, without interactive,
6566 In bare shelve (when no files are specified, without interactive,
6624 include and exclude option), shelving remembers information if the
6567 include and exclude option), shelving remembers information if the
6625 working directory was on newly created branch, in other words working
6568 working directory was on newly created branch, in other words working
6626 directory was on different branch than its first parent. In this
6569 directory was on different branch than its first parent. In this
6627 situation unshelving restores branch information to the working directory.
6570 situation unshelving restores branch information to the working directory.
6628
6571
6629 Each shelved change has a name that makes it easier to find later.
6572 Each shelved change has a name that makes it easier to find later.
6630 The name of a shelved change defaults to being based on the active
6573 The name of a shelved change defaults to being based on the active
6631 bookmark, or if there is no active bookmark, the current named
6574 bookmark, or if there is no active bookmark, the current named
6632 branch. To specify a different name, use ``--name``.
6575 branch. To specify a different name, use ``--name``.
6633
6576
6634 To see a list of existing shelved changes, use the ``--list``
6577 To see a list of existing shelved changes, use the ``--list``
6635 option. For each shelved change, this will print its name, age,
6578 option. For each shelved change, this will print its name, age,
6636 and description; use ``--patch`` or ``--stat`` for more details.
6579 and description; use ``--patch`` or ``--stat`` for more details.
6637
6580
6638 To delete specific shelved changes, use ``--delete``. To delete
6581 To delete specific shelved changes, use ``--delete``. To delete
6639 all shelved changes, use ``--cleanup``.
6582 all shelved changes, use ``--cleanup``.
6640 '''
6583 '''
6641 opts = pycompat.byteskwargs(opts)
6584 opts = pycompat.byteskwargs(opts)
6642 allowables = [
6585 allowables = [
6643 (b'addremove', {b'create'}), # 'create' is pseudo action
6586 (b'addremove', {b'create'}), # 'create' is pseudo action
6644 (b'unknown', {b'create'}),
6587 (b'unknown', {b'create'}),
6645 (b'cleanup', {b'cleanup'}),
6588 (b'cleanup', {b'cleanup'}),
6646 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6589 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6647 (b'delete', {b'delete'}),
6590 (b'delete', {b'delete'}),
6648 (b'edit', {b'create'}),
6591 (b'edit', {b'create'}),
6649 (b'keep', {b'create'}),
6592 (b'keep', {b'create'}),
6650 (b'list', {b'list'}),
6593 (b'list', {b'list'}),
6651 (b'message', {b'create'}),
6594 (b'message', {b'create'}),
6652 (b'name', {b'create'}),
6595 (b'name', {b'create'}),
6653 (b'patch', {b'patch', b'list'}),
6596 (b'patch', {b'patch', b'list'}),
6654 (b'stat', {b'stat', b'list'}),
6597 (b'stat', {b'stat', b'list'}),
6655 ]
6598 ]
6656
6599
6657 def checkopt(opt):
6600 def checkopt(opt):
6658 if opts.get(opt):
6601 if opts.get(opt):
6659 for i, allowable in allowables:
6602 for i, allowable in allowables:
6660 if opts[i] and opt not in allowable:
6603 if opts[i] and opt not in allowable:
6661 raise error.Abort(
6604 raise error.Abort(
6662 _(
6605 _(
6663 b"options '--%s' and '--%s' may not be "
6606 b"options '--%s' and '--%s' may not be "
6664 b"used together"
6607 b"used together"
6665 )
6608 )
6666 % (opt, i)
6609 % (opt, i)
6667 )
6610 )
6668 return True
6611 return True
6669
6612
6670 if checkopt(b'cleanup'):
6613 if checkopt(b'cleanup'):
6671 if pats:
6614 if pats:
6672 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6615 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6673 return shelvemod.cleanupcmd(ui, repo)
6616 return shelvemod.cleanupcmd(ui, repo)
6674 elif checkopt(b'delete'):
6617 elif checkopt(b'delete'):
6675 return shelvemod.deletecmd(ui, repo, pats)
6618 return shelvemod.deletecmd(ui, repo, pats)
6676 elif checkopt(b'list'):
6619 elif checkopt(b'list'):
6677 return shelvemod.listcmd(ui, repo, pats, opts)
6620 return shelvemod.listcmd(ui, repo, pats, opts)
6678 elif checkopt(b'patch') or checkopt(b'stat'):
6621 elif checkopt(b'patch') or checkopt(b'stat'):
6679 return shelvemod.patchcmds(ui, repo, pats, opts)
6622 return shelvemod.patchcmds(ui, repo, pats, opts)
6680 else:
6623 else:
6681 return shelvemod.createcmd(ui, repo, pats, opts)
6624 return shelvemod.createcmd(ui, repo, pats, opts)
6682
6625
6683
6626
6684 _NOTTERSE = b'nothing'
6627 _NOTTERSE = b'nothing'
6685
6628
6686
6629
6687 @command(
6630 @command(
6688 b'status|st',
6631 b'status|st',
6689 [
6632 [
6690 (b'A', b'all', None, _(b'show status of all files')),
6633 (b'A', b'all', None, _(b'show status of all files')),
6691 (b'm', b'modified', None, _(b'show only modified files')),
6634 (b'm', b'modified', None, _(b'show only modified files')),
6692 (b'a', b'added', None, _(b'show only added files')),
6635 (b'a', b'added', None, _(b'show only added files')),
6693 (b'r', b'removed', None, _(b'show only removed files')),
6636 (b'r', b'removed', None, _(b'show only removed files')),
6694 (b'd', b'deleted', None, _(b'show only missing files')),
6637 (b'd', b'deleted', None, _(b'show only missing files')),
6695 (b'c', b'clean', None, _(b'show only files without changes')),
6638 (b'c', b'clean', None, _(b'show only files without changes')),
6696 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6639 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6697 (b'i', b'ignored', None, _(b'show only ignored files')),
6640 (b'i', b'ignored', None, _(b'show only ignored files')),
6698 (b'n', b'no-status', None, _(b'hide status prefix')),
6641 (b'n', b'no-status', None, _(b'hide status prefix')),
6699 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6642 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6700 (
6643 (
6701 b'C',
6644 b'C',
6702 b'copies',
6645 b'copies',
6703 None,
6646 None,
6704 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6647 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6705 ),
6648 ),
6706 (
6649 (
6707 b'0',
6650 b'0',
6708 b'print0',
6651 b'print0',
6709 None,
6652 None,
6710 _(b'end filenames with NUL, for use with xargs'),
6653 _(b'end filenames with NUL, for use with xargs'),
6711 ),
6654 ),
6712 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6655 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6713 (
6656 (
6714 b'',
6657 b'',
6715 b'change',
6658 b'change',
6716 b'',
6659 b'',
6717 _(b'list the changed files of a revision'),
6660 _(b'list the changed files of a revision'),
6718 _(b'REV'),
6661 _(b'REV'),
6719 ),
6662 ),
6720 ]
6663 ]
6721 + walkopts
6664 + walkopts
6722 + subrepoopts
6665 + subrepoopts
6723 + formatteropts,
6666 + formatteropts,
6724 _(b'[OPTION]... [FILE]...'),
6667 _(b'[OPTION]... [FILE]...'),
6725 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6668 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6726 helpbasic=True,
6669 helpbasic=True,
6727 inferrepo=True,
6670 inferrepo=True,
6728 intents={INTENT_READONLY},
6671 intents={INTENT_READONLY},
6729 )
6672 )
6730 def status(ui, repo, *pats, **opts):
6673 def status(ui, repo, *pats, **opts):
6731 """show changed files in the working directory
6674 """show changed files in the working directory
6732
6675
6733 Show status of files in the repository. If names are given, only
6676 Show status of files in the repository. If names are given, only
6734 files that match are shown. Files that are clean or ignored or
6677 files that match are shown. Files that are clean or ignored or
6735 the source of a copy/move operation, are not listed unless
6678 the source of a copy/move operation, are not listed unless
6736 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6679 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6737 Unless options described with "show only ..." are given, the
6680 Unless options described with "show only ..." are given, the
6738 options -mardu are used.
6681 options -mardu are used.
6739
6682
6740 Option -q/--quiet hides untracked (unknown and ignored) files
6683 Option -q/--quiet hides untracked (unknown and ignored) files
6741 unless explicitly requested with -u/--unknown or -i/--ignored.
6684 unless explicitly requested with -u/--unknown or -i/--ignored.
6742
6685
6743 .. note::
6686 .. note::
6744
6687
6745 :hg:`status` may appear to disagree with diff if permissions have
6688 :hg:`status` may appear to disagree with diff if permissions have
6746 changed or a merge has occurred. The standard diff format does
6689 changed or a merge has occurred. The standard diff format does
6747 not report permission changes and diff only reports changes
6690 not report permission changes and diff only reports changes
6748 relative to one merge parent.
6691 relative to one merge parent.
6749
6692
6750 If one revision is given, it is used as the base revision.
6693 If one revision is given, it is used as the base revision.
6751 If two revisions are given, the differences between them are
6694 If two revisions are given, the differences between them are
6752 shown. The --change option can also be used as a shortcut to list
6695 shown. The --change option can also be used as a shortcut to list
6753 the changed files of a revision from its first parent.
6696 the changed files of a revision from its first parent.
6754
6697
6755 The codes used to show the status of files are::
6698 The codes used to show the status of files are::
6756
6699
6757 M = modified
6700 M = modified
6758 A = added
6701 A = added
6759 R = removed
6702 R = removed
6760 C = clean
6703 C = clean
6761 ! = missing (deleted by non-hg command, but still tracked)
6704 ! = missing (deleted by non-hg command, but still tracked)
6762 ? = not tracked
6705 ? = not tracked
6763 I = ignored
6706 I = ignored
6764 = origin of the previous file (with --copies)
6707 = origin of the previous file (with --copies)
6765
6708
6766 .. container:: verbose
6709 .. container:: verbose
6767
6710
6768 The -t/--terse option abbreviates the output by showing only the directory
6711 The -t/--terse option abbreviates the output by showing only the directory
6769 name if all the files in it share the same status. The option takes an
6712 name if all the files in it share the same status. The option takes an
6770 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6713 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6771 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6714 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6772 for 'ignored' and 'c' for clean.
6715 for 'ignored' and 'c' for clean.
6773
6716
6774 It abbreviates only those statuses which are passed. Note that clean and
6717 It abbreviates only those statuses which are passed. Note that clean and
6775 ignored files are not displayed with '--terse ic' unless the -c/--clean
6718 ignored files are not displayed with '--terse ic' unless the -c/--clean
6776 and -i/--ignored options are also used.
6719 and -i/--ignored options are also used.
6777
6720
6778 The -v/--verbose option shows information when the repository is in an
6721 The -v/--verbose option shows information when the repository is in an
6779 unfinished merge, shelve, rebase state etc. You can have this behavior
6722 unfinished merge, shelve, rebase state etc. You can have this behavior
6780 turned on by default by enabling the ``commands.status.verbose`` option.
6723 turned on by default by enabling the ``commands.status.verbose`` option.
6781
6724
6782 You can skip displaying some of these states by setting
6725 You can skip displaying some of these states by setting
6783 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6726 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6784 'histedit', 'merge', 'rebase', or 'unshelve'.
6727 'histedit', 'merge', 'rebase', or 'unshelve'.
6785
6728
6786 Template:
6729 Template:
6787
6730
6788 The following keywords are supported in addition to the common template
6731 The following keywords are supported in addition to the common template
6789 keywords and functions. See also :hg:`help templates`.
6732 keywords and functions. See also :hg:`help templates`.
6790
6733
6791 :path: String. Repository-absolute path of the file.
6734 :path: String. Repository-absolute path of the file.
6792 :source: String. Repository-absolute path of the file originated from.
6735 :source: String. Repository-absolute path of the file originated from.
6793 Available if ``--copies`` is specified.
6736 Available if ``--copies`` is specified.
6794 :status: String. Character denoting file's status.
6737 :status: String. Character denoting file's status.
6795
6738
6796 Examples:
6739 Examples:
6797
6740
6798 - show changes in the working directory relative to a
6741 - show changes in the working directory relative to a
6799 changeset::
6742 changeset::
6800
6743
6801 hg status --rev 9353
6744 hg status --rev 9353
6802
6745
6803 - show changes in the working directory relative to the
6746 - show changes in the working directory relative to the
6804 current directory (see :hg:`help patterns` for more information)::
6747 current directory (see :hg:`help patterns` for more information)::
6805
6748
6806 hg status re:
6749 hg status re:
6807
6750
6808 - show all changes including copies in an existing changeset::
6751 - show all changes including copies in an existing changeset::
6809
6752
6810 hg status --copies --change 9353
6753 hg status --copies --change 9353
6811
6754
6812 - get a NUL separated list of added files, suitable for xargs::
6755 - get a NUL separated list of added files, suitable for xargs::
6813
6756
6814 hg status -an0
6757 hg status -an0
6815
6758
6816 - show more information about the repository status, abbreviating
6759 - show more information about the repository status, abbreviating
6817 added, removed, modified, deleted, and untracked paths::
6760 added, removed, modified, deleted, and untracked paths::
6818
6761
6819 hg status -v -t mardu
6762 hg status -v -t mardu
6820
6763
6821 Returns 0 on success.
6764 Returns 0 on success.
6822
6765
6823 """
6766 """
6824
6767
6825 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6768 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6826 opts = pycompat.byteskwargs(opts)
6769 opts = pycompat.byteskwargs(opts)
6827 revs = opts.get(b'rev')
6770 revs = opts.get(b'rev')
6828 change = opts.get(b'change')
6771 change = opts.get(b'change')
6829 terse = opts.get(b'terse')
6772 terse = opts.get(b'terse')
6830 if terse is _NOTTERSE:
6773 if terse is _NOTTERSE:
6831 if revs:
6774 if revs:
6832 terse = b''
6775 terse = b''
6833 else:
6776 else:
6834 terse = ui.config(b'commands', b'status.terse')
6777 terse = ui.config(b'commands', b'status.terse')
6835
6778
6836 if revs and terse:
6779 if revs and terse:
6837 msg = _(b'cannot use --terse with --rev')
6780 msg = _(b'cannot use --terse with --rev')
6838 raise error.Abort(msg)
6781 raise error.Abort(msg)
6839 elif change:
6782 elif change:
6840 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6783 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6841 ctx2 = scmutil.revsingle(repo, change, None)
6784 ctx2 = scmutil.revsingle(repo, change, None)
6842 ctx1 = ctx2.p1()
6785 ctx1 = ctx2.p1()
6843 else:
6786 else:
6844 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6787 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6845 ctx1, ctx2 = scmutil.revpair(repo, revs)
6788 ctx1, ctx2 = scmutil.revpair(repo, revs)
6846
6789
6847 forcerelativevalue = None
6790 forcerelativevalue = None
6848 if ui.hasconfig(b'commands', b'status.relative'):
6791 if ui.hasconfig(b'commands', b'status.relative'):
6849 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6792 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6850 uipathfn = scmutil.getuipathfn(
6793 uipathfn = scmutil.getuipathfn(
6851 repo,
6794 repo,
6852 legacyrelativevalue=bool(pats),
6795 legacyrelativevalue=bool(pats),
6853 forcerelativevalue=forcerelativevalue,
6796 forcerelativevalue=forcerelativevalue,
6854 )
6797 )
6855
6798
6856 if opts.get(b'print0'):
6799 if opts.get(b'print0'):
6857 end = b'\0'
6800 end = b'\0'
6858 else:
6801 else:
6859 end = b'\n'
6802 end = b'\n'
6860 states = b'modified added removed deleted unknown ignored clean'.split()
6803 states = b'modified added removed deleted unknown ignored clean'.split()
6861 show = [k for k in states if opts.get(k)]
6804 show = [k for k in states if opts.get(k)]
6862 if opts.get(b'all'):
6805 if opts.get(b'all'):
6863 show += ui.quiet and (states[:4] + [b'clean']) or states
6806 show += ui.quiet and (states[:4] + [b'clean']) or states
6864
6807
6865 if not show:
6808 if not show:
6866 if ui.quiet:
6809 if ui.quiet:
6867 show = states[:4]
6810 show = states[:4]
6868 else:
6811 else:
6869 show = states[:5]
6812 show = states[:5]
6870
6813
6871 m = scmutil.match(ctx2, pats, opts)
6814 m = scmutil.match(ctx2, pats, opts)
6872 if terse:
6815 if terse:
6873 # we need to compute clean and unknown to terse
6816 # we need to compute clean and unknown to terse
6874 stat = repo.status(
6817 stat = repo.status(
6875 ctx1.node(),
6818 ctx1.node(),
6876 ctx2.node(),
6819 ctx2.node(),
6877 m,
6820 m,
6878 b'ignored' in show or b'i' in terse,
6821 b'ignored' in show or b'i' in terse,
6879 clean=True,
6822 clean=True,
6880 unknown=True,
6823 unknown=True,
6881 listsubrepos=opts.get(b'subrepos'),
6824 listsubrepos=opts.get(b'subrepos'),
6882 )
6825 )
6883
6826
6884 stat = cmdutil.tersedir(stat, terse)
6827 stat = cmdutil.tersedir(stat, terse)
6885 else:
6828 else:
6886 stat = repo.status(
6829 stat = repo.status(
6887 ctx1.node(),
6830 ctx1.node(),
6888 ctx2.node(),
6831 ctx2.node(),
6889 m,
6832 m,
6890 b'ignored' in show,
6833 b'ignored' in show,
6891 b'clean' in show,
6834 b'clean' in show,
6892 b'unknown' in show,
6835 b'unknown' in show,
6893 opts.get(b'subrepos'),
6836 opts.get(b'subrepos'),
6894 )
6837 )
6895
6838
6896 changestates = zip(
6839 changestates = zip(
6897 states,
6840 states,
6898 pycompat.iterbytestr(b'MAR!?IC'),
6841 pycompat.iterbytestr(b'MAR!?IC'),
6899 [getattr(stat, s.decode('utf8')) for s in states],
6842 [getattr(stat, s.decode('utf8')) for s in states],
6900 )
6843 )
6901
6844
6902 copy = {}
6845 copy = {}
6903 if (
6846 if (
6904 opts.get(b'all')
6847 opts.get(b'all')
6905 or opts.get(b'copies')
6848 or opts.get(b'copies')
6906 or ui.configbool(b'ui', b'statuscopies')
6849 or ui.configbool(b'ui', b'statuscopies')
6907 ) and not opts.get(b'no_status'):
6850 ) and not opts.get(b'no_status'):
6908 copy = copies.pathcopies(ctx1, ctx2, m)
6851 copy = copies.pathcopies(ctx1, ctx2, m)
6909
6852
6910 morestatus = None
6853 morestatus = None
6911 if (
6854 if (
6912 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6855 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6913 ) and not ui.plain():
6856 ) and not ui.plain():
6914 morestatus = cmdutil.readmorestatus(repo)
6857 morestatus = cmdutil.readmorestatus(repo)
6915
6858
6916 ui.pager(b'status')
6859 ui.pager(b'status')
6917 fm = ui.formatter(b'status', opts)
6860 fm = ui.formatter(b'status', opts)
6918 fmt = b'%s' + end
6861 fmt = b'%s' + end
6919 showchar = not opts.get(b'no_status')
6862 showchar = not opts.get(b'no_status')
6920
6863
6921 for state, char, files in changestates:
6864 for state, char, files in changestates:
6922 if state in show:
6865 if state in show:
6923 label = b'status.' + state
6866 label = b'status.' + state
6924 for f in files:
6867 for f in files:
6925 fm.startitem()
6868 fm.startitem()
6926 fm.context(ctx=ctx2)
6869 fm.context(ctx=ctx2)
6927 fm.data(itemtype=b'file', path=f)
6870 fm.data(itemtype=b'file', path=f)
6928 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6871 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6929 fm.plain(fmt % uipathfn(f), label=label)
6872 fm.plain(fmt % uipathfn(f), label=label)
6930 if f in copy:
6873 if f in copy:
6931 fm.data(source=copy[f])
6874 fm.data(source=copy[f])
6932 fm.plain(
6875 fm.plain(
6933 (b' %s' + end) % uipathfn(copy[f]),
6876 (b' %s' + end) % uipathfn(copy[f]),
6934 label=b'status.copied',
6877 label=b'status.copied',
6935 )
6878 )
6936 if morestatus:
6879 if morestatus:
6937 morestatus.formatfile(f, fm)
6880 morestatus.formatfile(f, fm)
6938
6881
6939 if morestatus:
6882 if morestatus:
6940 morestatus.formatfooter(fm)
6883 morestatus.formatfooter(fm)
6941 fm.end()
6884 fm.end()
6942
6885
6943
6886
6944 @command(
6887 @command(
6945 b'summary|sum',
6888 b'summary|sum',
6946 [(b'', b'remote', None, _(b'check for push and pull'))],
6889 [(b'', b'remote', None, _(b'check for push and pull'))],
6947 b'[--remote]',
6890 b'[--remote]',
6948 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6891 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6949 helpbasic=True,
6892 helpbasic=True,
6950 intents={INTENT_READONLY},
6893 intents={INTENT_READONLY},
6951 )
6894 )
6952 def summary(ui, repo, **opts):
6895 def summary(ui, repo, **opts):
6953 """summarize working directory state
6896 """summarize working directory state
6954
6897
6955 This generates a brief summary of the working directory state,
6898 This generates a brief summary of the working directory state,
6956 including parents, branch, commit status, phase and available updates.
6899 including parents, branch, commit status, phase and available updates.
6957
6900
6958 With the --remote option, this will check the default paths for
6901 With the --remote option, this will check the default paths for
6959 incoming and outgoing changes. This can be time-consuming.
6902 incoming and outgoing changes. This can be time-consuming.
6960
6903
6961 Returns 0 on success.
6904 Returns 0 on success.
6962 """
6905 """
6963
6906
6964 opts = pycompat.byteskwargs(opts)
6907 opts = pycompat.byteskwargs(opts)
6965 ui.pager(b'summary')
6908 ui.pager(b'summary')
6966 ctx = repo[None]
6909 ctx = repo[None]
6967 parents = ctx.parents()
6910 parents = ctx.parents()
6968 pnode = parents[0].node()
6911 pnode = parents[0].node()
6969 marks = []
6912 marks = []
6970
6913
6971 try:
6914 try:
6972 ms = mergestatemod.mergestate.read(repo)
6915 ms = mergestatemod.mergestate.read(repo)
6973 except error.UnsupportedMergeRecords as e:
6916 except error.UnsupportedMergeRecords as e:
6974 s = b' '.join(e.recordtypes)
6917 s = b' '.join(e.recordtypes)
6975 ui.warn(
6918 ui.warn(
6976 _(b'warning: merge state has unsupported record types: %s\n') % s
6919 _(b'warning: merge state has unsupported record types: %s\n') % s
6977 )
6920 )
6978 unresolved = []
6921 unresolved = []
6979 else:
6922 else:
6980 unresolved = list(ms.unresolved())
6923 unresolved = list(ms.unresolved())
6981
6924
6982 for p in parents:
6925 for p in parents:
6983 # label with log.changeset (instead of log.parent) since this
6926 # label with log.changeset (instead of log.parent) since this
6984 # shows a working directory parent *changeset*:
6927 # shows a working directory parent *changeset*:
6985 # i18n: column positioning for "hg summary"
6928 # i18n: column positioning for "hg summary"
6986 ui.write(
6929 ui.write(
6987 _(b'parent: %d:%s ') % (p.rev(), p),
6930 _(b'parent: %d:%s ') % (p.rev(), p),
6988 label=logcmdutil.changesetlabels(p),
6931 label=logcmdutil.changesetlabels(p),
6989 )
6932 )
6990 ui.write(b' '.join(p.tags()), label=b'log.tag')
6933 ui.write(b' '.join(p.tags()), label=b'log.tag')
6991 if p.bookmarks():
6934 if p.bookmarks():
6992 marks.extend(p.bookmarks())
6935 marks.extend(p.bookmarks())
6993 if p.rev() == -1:
6936 if p.rev() == -1:
6994 if not len(repo):
6937 if not len(repo):
6995 ui.write(_(b' (empty repository)'))
6938 ui.write(_(b' (empty repository)'))
6996 else:
6939 else:
6997 ui.write(_(b' (no revision checked out)'))
6940 ui.write(_(b' (no revision checked out)'))
6998 if p.obsolete():
6941 if p.obsolete():
6999 ui.write(_(b' (obsolete)'))
6942 ui.write(_(b' (obsolete)'))
7000 if p.isunstable():
6943 if p.isunstable():
7001 instabilities = (
6944 instabilities = (
7002 ui.label(instability, b'trouble.%s' % instability)
6945 ui.label(instability, b'trouble.%s' % instability)
7003 for instability in p.instabilities()
6946 for instability in p.instabilities()
7004 )
6947 )
7005 ui.write(b' (' + b', '.join(instabilities) + b')')
6948 ui.write(b' (' + b', '.join(instabilities) + b')')
7006 ui.write(b'\n')
6949 ui.write(b'\n')
7007 if p.description():
6950 if p.description():
7008 ui.status(
6951 ui.status(
7009 b' ' + p.description().splitlines()[0].strip() + b'\n',
6952 b' ' + p.description().splitlines()[0].strip() + b'\n',
7010 label=b'log.summary',
6953 label=b'log.summary',
7011 )
6954 )
7012
6955
7013 branch = ctx.branch()
6956 branch = ctx.branch()
7014 bheads = repo.branchheads(branch)
6957 bheads = repo.branchheads(branch)
7015 # i18n: column positioning for "hg summary"
6958 # i18n: column positioning for "hg summary"
7016 m = _(b'branch: %s\n') % branch
6959 m = _(b'branch: %s\n') % branch
7017 if branch != b'default':
6960 if branch != b'default':
7018 ui.write(m, label=b'log.branch')
6961 ui.write(m, label=b'log.branch')
7019 else:
6962 else:
7020 ui.status(m, label=b'log.branch')
6963 ui.status(m, label=b'log.branch')
7021
6964
7022 if marks:
6965 if marks:
7023 active = repo._activebookmark
6966 active = repo._activebookmark
7024 # i18n: column positioning for "hg summary"
6967 # i18n: column positioning for "hg summary"
7025 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6968 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7026 if active is not None:
6969 if active is not None:
7027 if active in marks:
6970 if active in marks:
7028 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6971 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7029 marks.remove(active)
6972 marks.remove(active)
7030 else:
6973 else:
7031 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6974 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7032 for m in marks:
6975 for m in marks:
7033 ui.write(b' ' + m, label=b'log.bookmark')
6976 ui.write(b' ' + m, label=b'log.bookmark')
7034 ui.write(b'\n', label=b'log.bookmark')
6977 ui.write(b'\n', label=b'log.bookmark')
7035
6978
7036 status = repo.status(unknown=True)
6979 status = repo.status(unknown=True)
7037
6980
7038 c = repo.dirstate.copies()
6981 c = repo.dirstate.copies()
7039 copied, renamed = [], []
6982 copied, renamed = [], []
7040 for d, s in pycompat.iteritems(c):
6983 for d, s in pycompat.iteritems(c):
7041 if s in status.removed:
6984 if s in status.removed:
7042 status.removed.remove(s)
6985 status.removed.remove(s)
7043 renamed.append(d)
6986 renamed.append(d)
7044 else:
6987 else:
7045 copied.append(d)
6988 copied.append(d)
7046 if d in status.added:
6989 if d in status.added:
7047 status.added.remove(d)
6990 status.added.remove(d)
7048
6991
7049 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6992 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7050
6993
7051 labels = [
6994 labels = [
7052 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6995 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7053 (ui.label(_(b'%d added'), b'status.added'), status.added),
6996 (ui.label(_(b'%d added'), b'status.added'), status.added),
7054 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6997 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7055 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6998 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7056 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6999 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7057 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7000 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7058 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7001 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7059 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7002 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7060 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7003 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7061 ]
7004 ]
7062 t = []
7005 t = []
7063 for l, s in labels:
7006 for l, s in labels:
7064 if s:
7007 if s:
7065 t.append(l % len(s))
7008 t.append(l % len(s))
7066
7009
7067 t = b', '.join(t)
7010 t = b', '.join(t)
7068 cleanworkdir = False
7011 cleanworkdir = False
7069
7012
7070 if repo.vfs.exists(b'graftstate'):
7013 if repo.vfs.exists(b'graftstate'):
7071 t += _(b' (graft in progress)')
7014 t += _(b' (graft in progress)')
7072 if repo.vfs.exists(b'updatestate'):
7015 if repo.vfs.exists(b'updatestate'):
7073 t += _(b' (interrupted update)')
7016 t += _(b' (interrupted update)')
7074 elif len(parents) > 1:
7017 elif len(parents) > 1:
7075 t += _(b' (merge)')
7018 t += _(b' (merge)')
7076 elif branch != parents[0].branch():
7019 elif branch != parents[0].branch():
7077 t += _(b' (new branch)')
7020 t += _(b' (new branch)')
7078 elif parents[0].closesbranch() and pnode in repo.branchheads(
7021 elif parents[0].closesbranch() and pnode in repo.branchheads(
7079 branch, closed=True
7022 branch, closed=True
7080 ):
7023 ):
7081 t += _(b' (head closed)')
7024 t += _(b' (head closed)')
7082 elif not (
7025 elif not (
7083 status.modified
7026 status.modified
7084 or status.added
7027 or status.added
7085 or status.removed
7028 or status.removed
7086 or renamed
7029 or renamed
7087 or copied
7030 or copied
7088 or subs
7031 or subs
7089 ):
7032 ):
7090 t += _(b' (clean)')
7033 t += _(b' (clean)')
7091 cleanworkdir = True
7034 cleanworkdir = True
7092 elif pnode not in bheads:
7035 elif pnode not in bheads:
7093 t += _(b' (new branch head)')
7036 t += _(b' (new branch head)')
7094
7037
7095 if parents:
7038 if parents:
7096 pendingphase = max(p.phase() for p in parents)
7039 pendingphase = max(p.phase() for p in parents)
7097 else:
7040 else:
7098 pendingphase = phases.public
7041 pendingphase = phases.public
7099
7042
7100 if pendingphase > phases.newcommitphase(ui):
7043 if pendingphase > phases.newcommitphase(ui):
7101 t += b' (%s)' % phases.phasenames[pendingphase]
7044 t += b' (%s)' % phases.phasenames[pendingphase]
7102
7045
7103 if cleanworkdir:
7046 if cleanworkdir:
7104 # i18n: column positioning for "hg summary"
7047 # i18n: column positioning for "hg summary"
7105 ui.status(_(b'commit: %s\n') % t.strip())
7048 ui.status(_(b'commit: %s\n') % t.strip())
7106 else:
7049 else:
7107 # i18n: column positioning for "hg summary"
7050 # i18n: column positioning for "hg summary"
7108 ui.write(_(b'commit: %s\n') % t.strip())
7051 ui.write(_(b'commit: %s\n') % t.strip())
7109
7052
7110 # all ancestors of branch heads - all ancestors of parent = new csets
7053 # all ancestors of branch heads - all ancestors of parent = new csets
7111 new = len(
7054 new = len(
7112 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7055 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7113 )
7056 )
7114
7057
7115 if new == 0:
7058 if new == 0:
7116 # i18n: column positioning for "hg summary"
7059 # i18n: column positioning for "hg summary"
7117 ui.status(_(b'update: (current)\n'))
7060 ui.status(_(b'update: (current)\n'))
7118 elif pnode not in bheads:
7061 elif pnode not in bheads:
7119 # i18n: column positioning for "hg summary"
7062 # i18n: column positioning for "hg summary"
7120 ui.write(_(b'update: %d new changesets (update)\n') % new)
7063 ui.write(_(b'update: %d new changesets (update)\n') % new)
7121 else:
7064 else:
7122 # i18n: column positioning for "hg summary"
7065 # i18n: column positioning for "hg summary"
7123 ui.write(
7066 ui.write(
7124 _(b'update: %d new changesets, %d branch heads (merge)\n')
7067 _(b'update: %d new changesets, %d branch heads (merge)\n')
7125 % (new, len(bheads))
7068 % (new, len(bheads))
7126 )
7069 )
7127
7070
7128 t = []
7071 t = []
7129 draft = len(repo.revs(b'draft()'))
7072 draft = len(repo.revs(b'draft()'))
7130 if draft:
7073 if draft:
7131 t.append(_(b'%d draft') % draft)
7074 t.append(_(b'%d draft') % draft)
7132 secret = len(repo.revs(b'secret()'))
7075 secret = len(repo.revs(b'secret()'))
7133 if secret:
7076 if secret:
7134 t.append(_(b'%d secret') % secret)
7077 t.append(_(b'%d secret') % secret)
7135
7078
7136 if draft or secret:
7079 if draft or secret:
7137 ui.status(_(b'phases: %s\n') % b', '.join(t))
7080 ui.status(_(b'phases: %s\n') % b', '.join(t))
7138
7081
7139 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7082 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7140 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7083 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7141 numtrouble = len(repo.revs(trouble + b"()"))
7084 numtrouble = len(repo.revs(trouble + b"()"))
7142 # We write all the possibilities to ease translation
7085 # We write all the possibilities to ease translation
7143 troublemsg = {
7086 troublemsg = {
7144 b"orphan": _(b"orphan: %d changesets"),
7087 b"orphan": _(b"orphan: %d changesets"),
7145 b"contentdivergent": _(b"content-divergent: %d changesets"),
7088 b"contentdivergent": _(b"content-divergent: %d changesets"),
7146 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7089 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7147 }
7090 }
7148 if numtrouble > 0:
7091 if numtrouble > 0:
7149 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7092 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7150
7093
7151 cmdutil.summaryhooks(ui, repo)
7094 cmdutil.summaryhooks(ui, repo)
7152
7095
7153 if opts.get(b'remote'):
7096 if opts.get(b'remote'):
7154 needsincoming, needsoutgoing = True, True
7097 needsincoming, needsoutgoing = True, True
7155 else:
7098 else:
7156 needsincoming, needsoutgoing = False, False
7099 needsincoming, needsoutgoing = False, False
7157 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7100 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7158 if i:
7101 if i:
7159 needsincoming = True
7102 needsincoming = True
7160 if o:
7103 if o:
7161 needsoutgoing = True
7104 needsoutgoing = True
7162 if not needsincoming and not needsoutgoing:
7105 if not needsincoming and not needsoutgoing:
7163 return
7106 return
7164
7107
7165 def getincoming():
7108 def getincoming():
7166 source, branches = hg.parseurl(ui.expandpath(b'default'))
7109 source, branches = hg.parseurl(ui.expandpath(b'default'))
7167 sbranch = branches[0]
7110 sbranch = branches[0]
7168 try:
7111 try:
7169 other = hg.peer(repo, {}, source)
7112 other = hg.peer(repo, {}, source)
7170 except error.RepoError:
7113 except error.RepoError:
7171 if opts.get(b'remote'):
7114 if opts.get(b'remote'):
7172 raise
7115 raise
7173 return source, sbranch, None, None, None
7116 return source, sbranch, None, None, None
7174 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7117 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7175 if revs:
7118 if revs:
7176 revs = [other.lookup(rev) for rev in revs]
7119 revs = [other.lookup(rev) for rev in revs]
7177 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7120 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7178 repo.ui.pushbuffer()
7121 repo.ui.pushbuffer()
7179 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7122 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7180 repo.ui.popbuffer()
7123 repo.ui.popbuffer()
7181 return source, sbranch, other, commoninc, commoninc[1]
7124 return source, sbranch, other, commoninc, commoninc[1]
7182
7125
7183 if needsincoming:
7126 if needsincoming:
7184 source, sbranch, sother, commoninc, incoming = getincoming()
7127 source, sbranch, sother, commoninc, incoming = getincoming()
7185 else:
7128 else:
7186 source = sbranch = sother = commoninc = incoming = None
7129 source = sbranch = sother = commoninc = incoming = None
7187
7130
7188 def getoutgoing():
7131 def getoutgoing():
7189 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7132 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7190 dbranch = branches[0]
7133 dbranch = branches[0]
7191 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7134 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7192 if source != dest:
7135 if source != dest:
7193 try:
7136 try:
7194 dother = hg.peer(repo, {}, dest)
7137 dother = hg.peer(repo, {}, dest)
7195 except error.RepoError:
7138 except error.RepoError:
7196 if opts.get(b'remote'):
7139 if opts.get(b'remote'):
7197 raise
7140 raise
7198 return dest, dbranch, None, None
7141 return dest, dbranch, None, None
7199 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7142 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7200 elif sother is None:
7143 elif sother is None:
7201 # there is no explicit destination peer, but source one is invalid
7144 # there is no explicit destination peer, but source one is invalid
7202 return dest, dbranch, None, None
7145 return dest, dbranch, None, None
7203 else:
7146 else:
7204 dother = sother
7147 dother = sother
7205 if source != dest or (sbranch is not None and sbranch != dbranch):
7148 if source != dest or (sbranch is not None and sbranch != dbranch):
7206 common = None
7149 common = None
7207 else:
7150 else:
7208 common = commoninc
7151 common = commoninc
7209 if revs:
7152 if revs:
7210 revs = [repo.lookup(rev) for rev in revs]
7153 revs = [repo.lookup(rev) for rev in revs]
7211 repo.ui.pushbuffer()
7154 repo.ui.pushbuffer()
7212 outgoing = discovery.findcommonoutgoing(
7155 outgoing = discovery.findcommonoutgoing(
7213 repo, dother, onlyheads=revs, commoninc=common
7156 repo, dother, onlyheads=revs, commoninc=common
7214 )
7157 )
7215 repo.ui.popbuffer()
7158 repo.ui.popbuffer()
7216 return dest, dbranch, dother, outgoing
7159 return dest, dbranch, dother, outgoing
7217
7160
7218 if needsoutgoing:
7161 if needsoutgoing:
7219 dest, dbranch, dother, outgoing = getoutgoing()
7162 dest, dbranch, dother, outgoing = getoutgoing()
7220 else:
7163 else:
7221 dest = dbranch = dother = outgoing = None
7164 dest = dbranch = dother = outgoing = None
7222
7165
7223 if opts.get(b'remote'):
7166 if opts.get(b'remote'):
7224 t = []
7167 t = []
7225 if incoming:
7168 if incoming:
7226 t.append(_(b'1 or more incoming'))
7169 t.append(_(b'1 or more incoming'))
7227 o = outgoing.missing
7170 o = outgoing.missing
7228 if o:
7171 if o:
7229 t.append(_(b'%d outgoing') % len(o))
7172 t.append(_(b'%d outgoing') % len(o))
7230 other = dother or sother
7173 other = dother or sother
7231 if b'bookmarks' in other.listkeys(b'namespaces'):
7174 if b'bookmarks' in other.listkeys(b'namespaces'):
7232 counts = bookmarks.summary(repo, other)
7175 counts = bookmarks.summary(repo, other)
7233 if counts[0] > 0:
7176 if counts[0] > 0:
7234 t.append(_(b'%d incoming bookmarks') % counts[0])
7177 t.append(_(b'%d incoming bookmarks') % counts[0])
7235 if counts[1] > 0:
7178 if counts[1] > 0:
7236 t.append(_(b'%d outgoing bookmarks') % counts[1])
7179 t.append(_(b'%d outgoing bookmarks') % counts[1])
7237
7180
7238 if t:
7181 if t:
7239 # i18n: column positioning for "hg summary"
7182 # i18n: column positioning for "hg summary"
7240 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7183 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7241 else:
7184 else:
7242 # i18n: column positioning for "hg summary"
7185 # i18n: column positioning for "hg summary"
7243 ui.status(_(b'remote: (synced)\n'))
7186 ui.status(_(b'remote: (synced)\n'))
7244
7187
7245 cmdutil.summaryremotehooks(
7188 cmdutil.summaryremotehooks(
7246 ui,
7189 ui,
7247 repo,
7190 repo,
7248 opts,
7191 opts,
7249 (
7192 (
7250 (source, sbranch, sother, commoninc),
7193 (source, sbranch, sother, commoninc),
7251 (dest, dbranch, dother, outgoing),
7194 (dest, dbranch, dother, outgoing),
7252 ),
7195 ),
7253 )
7196 )
7254
7197
7255
7198
7256 @command(
7199 @command(
7257 b'tag',
7200 b'tag',
7258 [
7201 [
7259 (b'f', b'force', None, _(b'force tag')),
7202 (b'f', b'force', None, _(b'force tag')),
7260 (b'l', b'local', None, _(b'make the tag local')),
7203 (b'l', b'local', None, _(b'make the tag local')),
7261 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7204 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7262 (b'', b'remove', None, _(b'remove a tag')),
7205 (b'', b'remove', None, _(b'remove a tag')),
7263 # -l/--local is already there, commitopts cannot be used
7206 # -l/--local is already there, commitopts cannot be used
7264 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7207 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7265 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7208 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7266 ]
7209 ]
7267 + commitopts2,
7210 + commitopts2,
7268 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7211 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7269 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7212 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7270 )
7213 )
7271 def tag(ui, repo, name1, *names, **opts):
7214 def tag(ui, repo, name1, *names, **opts):
7272 """add one or more tags for the current or given revision
7215 """add one or more tags for the current or given revision
7273
7216
7274 Name a particular revision using <name>.
7217 Name a particular revision using <name>.
7275
7218
7276 Tags are used to name particular revisions of the repository and are
7219 Tags are used to name particular revisions of the repository and are
7277 very useful to compare different revisions, to go back to significant
7220 very useful to compare different revisions, to go back to significant
7278 earlier versions or to mark branch points as releases, etc. Changing
7221 earlier versions or to mark branch points as releases, etc. Changing
7279 an existing tag is normally disallowed; use -f/--force to override.
7222 an existing tag is normally disallowed; use -f/--force to override.
7280
7223
7281 If no revision is given, the parent of the working directory is
7224 If no revision is given, the parent of the working directory is
7282 used.
7225 used.
7283
7226
7284 To facilitate version control, distribution, and merging of tags,
7227 To facilitate version control, distribution, and merging of tags,
7285 they are stored as a file named ".hgtags" which is managed similarly
7228 they are stored as a file named ".hgtags" which is managed similarly
7286 to other project files and can be hand-edited if necessary. This
7229 to other project files and can be hand-edited if necessary. This
7287 also means that tagging creates a new commit. The file
7230 also means that tagging creates a new commit. The file
7288 ".hg/localtags" is used for local tags (not shared among
7231 ".hg/localtags" is used for local tags (not shared among
7289 repositories).
7232 repositories).
7290
7233
7291 Tag commits are usually made at the head of a branch. If the parent
7234 Tag commits are usually made at the head of a branch. If the parent
7292 of the working directory is not a branch head, :hg:`tag` aborts; use
7235 of the working directory is not a branch head, :hg:`tag` aborts; use
7293 -f/--force to force the tag commit to be based on a non-head
7236 -f/--force to force the tag commit to be based on a non-head
7294 changeset.
7237 changeset.
7295
7238
7296 See :hg:`help dates` for a list of formats valid for -d/--date.
7239 See :hg:`help dates` for a list of formats valid for -d/--date.
7297
7240
7298 Since tag names have priority over branch names during revision
7241 Since tag names have priority over branch names during revision
7299 lookup, using an existing branch name as a tag name is discouraged.
7242 lookup, using an existing branch name as a tag name is discouraged.
7300
7243
7301 Returns 0 on success.
7244 Returns 0 on success.
7302 """
7245 """
7303 opts = pycompat.byteskwargs(opts)
7246 opts = pycompat.byteskwargs(opts)
7304 with repo.wlock(), repo.lock():
7247 with repo.wlock(), repo.lock():
7305 rev_ = b"."
7248 rev_ = b"."
7306 names = [t.strip() for t in (name1,) + names]
7249 names = [t.strip() for t in (name1,) + names]
7307 if len(names) != len(set(names)):
7250 if len(names) != len(set(names)):
7308 raise error.Abort(_(b'tag names must be unique'))
7251 raise error.Abort(_(b'tag names must be unique'))
7309 for n in names:
7252 for n in names:
7310 scmutil.checknewlabel(repo, n, b'tag')
7253 scmutil.checknewlabel(repo, n, b'tag')
7311 if not n:
7254 if not n:
7312 raise error.Abort(
7255 raise error.Abort(
7313 _(b'tag names cannot consist entirely of whitespace')
7256 _(b'tag names cannot consist entirely of whitespace')
7314 )
7257 )
7315 if opts.get(b'rev') and opts.get(b'remove'):
7258 if opts.get(b'rev') and opts.get(b'remove'):
7316 raise error.Abort(_(b"--rev and --remove are incompatible"))
7259 raise error.Abort(_(b"--rev and --remove are incompatible"))
7317 if opts.get(b'rev'):
7260 if opts.get(b'rev'):
7318 rev_ = opts[b'rev']
7261 rev_ = opts[b'rev']
7319 message = opts.get(b'message')
7262 message = opts.get(b'message')
7320 if opts.get(b'remove'):
7263 if opts.get(b'remove'):
7321 if opts.get(b'local'):
7264 if opts.get(b'local'):
7322 expectedtype = b'local'
7265 expectedtype = b'local'
7323 else:
7266 else:
7324 expectedtype = b'global'
7267 expectedtype = b'global'
7325
7268
7326 for n in names:
7269 for n in names:
7327 if repo.tagtype(n) == b'global':
7270 if repo.tagtype(n) == b'global':
7328 alltags = tagsmod.findglobaltags(ui, repo)
7271 alltags = tagsmod.findglobaltags(ui, repo)
7329 if alltags[n][0] == nullid:
7272 if alltags[n][0] == nullid:
7330 raise error.Abort(_(b"tag '%s' is already removed") % n)
7273 raise error.Abort(_(b"tag '%s' is already removed") % n)
7331 if not repo.tagtype(n):
7274 if not repo.tagtype(n):
7332 raise error.Abort(_(b"tag '%s' does not exist") % n)
7275 raise error.Abort(_(b"tag '%s' does not exist") % n)
7333 if repo.tagtype(n) != expectedtype:
7276 if repo.tagtype(n) != expectedtype:
7334 if expectedtype == b'global':
7277 if expectedtype == b'global':
7335 raise error.Abort(
7278 raise error.Abort(
7336 _(b"tag '%s' is not a global tag") % n
7279 _(b"tag '%s' is not a global tag") % n
7337 )
7280 )
7338 else:
7281 else:
7339 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7282 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7340 rev_ = b'null'
7283 rev_ = b'null'
7341 if not message:
7284 if not message:
7342 # we don't translate commit messages
7285 # we don't translate commit messages
7343 message = b'Removed tag %s' % b', '.join(names)
7286 message = b'Removed tag %s' % b', '.join(names)
7344 elif not opts.get(b'force'):
7287 elif not opts.get(b'force'):
7345 for n in names:
7288 for n in names:
7346 if n in repo.tags():
7289 if n in repo.tags():
7347 raise error.Abort(
7290 raise error.Abort(
7348 _(b"tag '%s' already exists (use -f to force)") % n
7291 _(b"tag '%s' already exists (use -f to force)") % n
7349 )
7292 )
7350 if not opts.get(b'local'):
7293 if not opts.get(b'local'):
7351 p1, p2 = repo.dirstate.parents()
7294 p1, p2 = repo.dirstate.parents()
7352 if p2 != nullid:
7295 if p2 != nullid:
7353 raise error.Abort(_(b'uncommitted merge'))
7296 raise error.Abort(_(b'uncommitted merge'))
7354 bheads = repo.branchheads()
7297 bheads = repo.branchheads()
7355 if not opts.get(b'force') and bheads and p1 not in bheads:
7298 if not opts.get(b'force') and bheads and p1 not in bheads:
7356 raise error.Abort(
7299 raise error.Abort(
7357 _(
7300 _(
7358 b'working directory is not at a branch head '
7301 b'working directory is not at a branch head '
7359 b'(use -f to force)'
7302 b'(use -f to force)'
7360 )
7303 )
7361 )
7304 )
7362 node = scmutil.revsingle(repo, rev_).node()
7305 node = scmutil.revsingle(repo, rev_).node()
7363
7306
7364 if not message:
7307 if not message:
7365 # we don't translate commit messages
7308 # we don't translate commit messages
7366 message = b'Added tag %s for changeset %s' % (
7309 message = b'Added tag %s for changeset %s' % (
7367 b', '.join(names),
7310 b', '.join(names),
7368 short(node),
7311 short(node),
7369 )
7312 )
7370
7313
7371 date = opts.get(b'date')
7314 date = opts.get(b'date')
7372 if date:
7315 if date:
7373 date = dateutil.parsedate(date)
7316 date = dateutil.parsedate(date)
7374
7317
7375 if opts.get(b'remove'):
7318 if opts.get(b'remove'):
7376 editform = b'tag.remove'
7319 editform = b'tag.remove'
7377 else:
7320 else:
7378 editform = b'tag.add'
7321 editform = b'tag.add'
7379 editor = cmdutil.getcommiteditor(
7322 editor = cmdutil.getcommiteditor(
7380 editform=editform, **pycompat.strkwargs(opts)
7323 editform=editform, **pycompat.strkwargs(opts)
7381 )
7324 )
7382
7325
7383 # don't allow tagging the null rev
7326 # don't allow tagging the null rev
7384 if (
7327 if (
7385 not opts.get(b'remove')
7328 not opts.get(b'remove')
7386 and scmutil.revsingle(repo, rev_).rev() == nullrev
7329 and scmutil.revsingle(repo, rev_).rev() == nullrev
7387 ):
7330 ):
7388 raise error.Abort(_(b"cannot tag null revision"))
7331 raise error.Abort(_(b"cannot tag null revision"))
7389
7332
7390 tagsmod.tag(
7333 tagsmod.tag(
7391 repo,
7334 repo,
7392 names,
7335 names,
7393 node,
7336 node,
7394 message,
7337 message,
7395 opts.get(b'local'),
7338 opts.get(b'local'),
7396 opts.get(b'user'),
7339 opts.get(b'user'),
7397 date,
7340 date,
7398 editor=editor,
7341 editor=editor,
7399 )
7342 )
7400
7343
7401
7344
7402 @command(
7345 @command(
7403 b'tags',
7346 b'tags',
7404 formatteropts,
7347 formatteropts,
7405 b'',
7348 b'',
7406 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7349 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7407 intents={INTENT_READONLY},
7350 intents={INTENT_READONLY},
7408 )
7351 )
7409 def tags(ui, repo, **opts):
7352 def tags(ui, repo, **opts):
7410 """list repository tags
7353 """list repository tags
7411
7354
7412 This lists both regular and local tags. When the -v/--verbose
7355 This lists both regular and local tags. When the -v/--verbose
7413 switch is used, a third column "local" is printed for local tags.
7356 switch is used, a third column "local" is printed for local tags.
7414 When the -q/--quiet switch is used, only the tag name is printed.
7357 When the -q/--quiet switch is used, only the tag name is printed.
7415
7358
7416 .. container:: verbose
7359 .. container:: verbose
7417
7360
7418 Template:
7361 Template:
7419
7362
7420 The following keywords are supported in addition to the common template
7363 The following keywords are supported in addition to the common template
7421 keywords and functions such as ``{tag}``. See also
7364 keywords and functions such as ``{tag}``. See also
7422 :hg:`help templates`.
7365 :hg:`help templates`.
7423
7366
7424 :type: String. ``local`` for local tags.
7367 :type: String. ``local`` for local tags.
7425
7368
7426 Returns 0 on success.
7369 Returns 0 on success.
7427 """
7370 """
7428
7371
7429 opts = pycompat.byteskwargs(opts)
7372 opts = pycompat.byteskwargs(opts)
7430 ui.pager(b'tags')
7373 ui.pager(b'tags')
7431 fm = ui.formatter(b'tags', opts)
7374 fm = ui.formatter(b'tags', opts)
7432 hexfunc = fm.hexfunc
7375 hexfunc = fm.hexfunc
7433
7376
7434 for t, n in reversed(repo.tagslist()):
7377 for t, n in reversed(repo.tagslist()):
7435 hn = hexfunc(n)
7378 hn = hexfunc(n)
7436 label = b'tags.normal'
7379 label = b'tags.normal'
7437 tagtype = b''
7380 tagtype = b''
7438 if repo.tagtype(t) == b'local':
7381 if repo.tagtype(t) == b'local':
7439 label = b'tags.local'
7382 label = b'tags.local'
7440 tagtype = b'local'
7383 tagtype = b'local'
7441
7384
7442 fm.startitem()
7385 fm.startitem()
7443 fm.context(repo=repo)
7386 fm.context(repo=repo)
7444 fm.write(b'tag', b'%s', t, label=label)
7387 fm.write(b'tag', b'%s', t, label=label)
7445 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7388 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7446 fm.condwrite(
7389 fm.condwrite(
7447 not ui.quiet,
7390 not ui.quiet,
7448 b'rev node',
7391 b'rev node',
7449 fmt,
7392 fmt,
7450 repo.changelog.rev(n),
7393 repo.changelog.rev(n),
7451 hn,
7394 hn,
7452 label=label,
7395 label=label,
7453 )
7396 )
7454 fm.condwrite(
7397 fm.condwrite(
7455 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7398 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7456 )
7399 )
7457 fm.plain(b'\n')
7400 fm.plain(b'\n')
7458 fm.end()
7401 fm.end()
7459
7402
7460
7403
7461 @command(
7404 @command(
7462 b'tip',
7405 b'tip',
7463 [
7406 [
7464 (b'p', b'patch', None, _(b'show patch')),
7407 (b'p', b'patch', None, _(b'show patch')),
7465 (b'g', b'git', None, _(b'use git extended diff format')),
7408 (b'g', b'git', None, _(b'use git extended diff format')),
7466 ]
7409 ]
7467 + templateopts,
7410 + templateopts,
7468 _(b'[-p] [-g]'),
7411 _(b'[-p] [-g]'),
7469 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7412 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7470 )
7413 )
7471 def tip(ui, repo, **opts):
7414 def tip(ui, repo, **opts):
7472 """show the tip revision (DEPRECATED)
7415 """show the tip revision (DEPRECATED)
7473
7416
7474 The tip revision (usually just called the tip) is the changeset
7417 The tip revision (usually just called the tip) is the changeset
7475 most recently added to the repository (and therefore the most
7418 most recently added to the repository (and therefore the most
7476 recently changed head).
7419 recently changed head).
7477
7420
7478 If you have just made a commit, that commit will be the tip. If
7421 If you have just made a commit, that commit will be the tip. If
7479 you have just pulled changes from another repository, the tip of
7422 you have just pulled changes from another repository, the tip of
7480 that repository becomes the current tip. The "tip" tag is special
7423 that repository becomes the current tip. The "tip" tag is special
7481 and cannot be renamed or assigned to a different changeset.
7424 and cannot be renamed or assigned to a different changeset.
7482
7425
7483 This command is deprecated, please use :hg:`heads` instead.
7426 This command is deprecated, please use :hg:`heads` instead.
7484
7427
7485 Returns 0 on success.
7428 Returns 0 on success.
7486 """
7429 """
7487 opts = pycompat.byteskwargs(opts)
7430 opts = pycompat.byteskwargs(opts)
7488 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7431 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7489 displayer.show(repo[b'tip'])
7432 displayer.show(repo[b'tip'])
7490 displayer.close()
7433 displayer.close()
7491
7434
7492
7435
7493 @command(
7436 @command(
7494 b'unbundle',
7437 b'unbundle',
7495 [
7438 [
7496 (
7439 (
7497 b'u',
7440 b'u',
7498 b'update',
7441 b'update',
7499 None,
7442 None,
7500 _(b'update to new branch head if changesets were unbundled'),
7443 _(b'update to new branch head if changesets were unbundled'),
7501 )
7444 )
7502 ],
7445 ],
7503 _(b'[-u] FILE...'),
7446 _(b'[-u] FILE...'),
7504 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7447 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7505 )
7448 )
7506 def unbundle(ui, repo, fname1, *fnames, **opts):
7449 def unbundle(ui, repo, fname1, *fnames, **opts):
7507 """apply one or more bundle files
7450 """apply one or more bundle files
7508
7451
7509 Apply one or more bundle files generated by :hg:`bundle`.
7452 Apply one or more bundle files generated by :hg:`bundle`.
7510
7453
7511 Returns 0 on success, 1 if an update has unresolved files.
7454 Returns 0 on success, 1 if an update has unresolved files.
7512 """
7455 """
7513 fnames = (fname1,) + fnames
7456 fnames = (fname1,) + fnames
7514
7457
7515 with repo.lock():
7458 with repo.lock():
7516 for fname in fnames:
7459 for fname in fnames:
7517 f = hg.openpath(ui, fname)
7460 f = hg.openpath(ui, fname)
7518 gen = exchange.readbundle(ui, f, fname)
7461 gen = exchange.readbundle(ui, f, fname)
7519 if isinstance(gen, streamclone.streamcloneapplier):
7462 if isinstance(gen, streamclone.streamcloneapplier):
7520 raise error.Abort(
7463 raise error.Abort(
7521 _(
7464 _(
7522 b'packed bundles cannot be applied with '
7465 b'packed bundles cannot be applied with '
7523 b'"hg unbundle"'
7466 b'"hg unbundle"'
7524 ),
7467 ),
7525 hint=_(b'use "hg debugapplystreamclonebundle"'),
7468 hint=_(b'use "hg debugapplystreamclonebundle"'),
7526 )
7469 )
7527 url = b'bundle:' + fname
7470 url = b'bundle:' + fname
7528 try:
7471 try:
7529 txnname = b'unbundle'
7472 txnname = b'unbundle'
7530 if not isinstance(gen, bundle2.unbundle20):
7473 if not isinstance(gen, bundle2.unbundle20):
7531 txnname = b'unbundle\n%s' % util.hidepassword(url)
7474 txnname = b'unbundle\n%s' % util.hidepassword(url)
7532 with repo.transaction(txnname) as tr:
7475 with repo.transaction(txnname) as tr:
7533 op = bundle2.applybundle(
7476 op = bundle2.applybundle(
7534 repo, gen, tr, source=b'unbundle', url=url
7477 repo, gen, tr, source=b'unbundle', url=url
7535 )
7478 )
7536 except error.BundleUnknownFeatureError as exc:
7479 except error.BundleUnknownFeatureError as exc:
7537 raise error.Abort(
7480 raise error.Abort(
7538 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7481 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7539 hint=_(
7482 hint=_(
7540 b"see https://mercurial-scm.org/"
7483 b"see https://mercurial-scm.org/"
7541 b"wiki/BundleFeature for more "
7484 b"wiki/BundleFeature for more "
7542 b"information"
7485 b"information"
7543 ),
7486 ),
7544 )
7487 )
7545 modheads = bundle2.combinechangegroupresults(op)
7488 modheads = bundle2.combinechangegroupresults(op)
7546
7489
7547 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7490 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7548
7491
7549
7492
7550 @command(
7493 @command(
7551 b'unshelve',
7494 b'unshelve',
7552 [
7495 [
7553 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7496 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7554 (
7497 (
7555 b'c',
7498 b'c',
7556 b'continue',
7499 b'continue',
7557 None,
7500 None,
7558 _(b'continue an incomplete unshelve operation'),
7501 _(b'continue an incomplete unshelve operation'),
7559 ),
7502 ),
7560 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7503 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7561 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7504 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7562 (
7505 (
7563 b'n',
7506 b'n',
7564 b'name',
7507 b'name',
7565 b'',
7508 b'',
7566 _(b'restore shelved change with given name'),
7509 _(b'restore shelved change with given name'),
7567 _(b'NAME'),
7510 _(b'NAME'),
7568 ),
7511 ),
7569 (b't', b'tool', b'', _(b'specify merge tool')),
7512 (b't', b'tool', b'', _(b'specify merge tool')),
7570 (
7513 (
7571 b'',
7514 b'',
7572 b'date',
7515 b'date',
7573 b'',
7516 b'',
7574 _(b'set date for temporary commits (DEPRECATED)'),
7517 _(b'set date for temporary commits (DEPRECATED)'),
7575 _(b'DATE'),
7518 _(b'DATE'),
7576 ),
7519 ),
7577 ],
7520 ],
7578 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7521 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7579 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7522 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7580 )
7523 )
7581 def unshelve(ui, repo, *shelved, **opts):
7524 def unshelve(ui, repo, *shelved, **opts):
7582 """restore a shelved change to the working directory
7525 """restore a shelved change to the working directory
7583
7526
7584 This command accepts an optional name of a shelved change to
7527 This command accepts an optional name of a shelved change to
7585 restore. If none is given, the most recent shelved change is used.
7528 restore. If none is given, the most recent shelved change is used.
7586
7529
7587 If a shelved change is applied successfully, the bundle that
7530 If a shelved change is applied successfully, the bundle that
7588 contains the shelved changes is moved to a backup location
7531 contains the shelved changes is moved to a backup location
7589 (.hg/shelve-backup).
7532 (.hg/shelve-backup).
7590
7533
7591 Since you can restore a shelved change on top of an arbitrary
7534 Since you can restore a shelved change on top of an arbitrary
7592 commit, it is possible that unshelving will result in a conflict
7535 commit, it is possible that unshelving will result in a conflict
7593 between your changes and the commits you are unshelving onto. If
7536 between your changes and the commits you are unshelving onto. If
7594 this occurs, you must resolve the conflict, then use
7537 this occurs, you must resolve the conflict, then use
7595 ``--continue`` to complete the unshelve operation. (The bundle
7538 ``--continue`` to complete the unshelve operation. (The bundle
7596 will not be moved until you successfully complete the unshelve.)
7539 will not be moved until you successfully complete the unshelve.)
7597
7540
7598 (Alternatively, you can use ``--abort`` to abandon an unshelve
7541 (Alternatively, you can use ``--abort`` to abandon an unshelve
7599 that causes a conflict. This reverts the unshelved changes, and
7542 that causes a conflict. This reverts the unshelved changes, and
7600 leaves the bundle in place.)
7543 leaves the bundle in place.)
7601
7544
7602 If bare shelved change (without interactive, include and exclude
7545 If bare shelved change (without interactive, include and exclude
7603 option) was done on newly created branch it would restore branch
7546 option) was done on newly created branch it would restore branch
7604 information to the working directory.
7547 information to the working directory.
7605
7548
7606 After a successful unshelve, the shelved changes are stored in a
7549 After a successful unshelve, the shelved changes are stored in a
7607 backup directory. Only the N most recent backups are kept. N
7550 backup directory. Only the N most recent backups are kept. N
7608 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7551 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7609 configuration option.
7552 configuration option.
7610
7553
7611 .. container:: verbose
7554 .. container:: verbose
7612
7555
7613 Timestamp in seconds is used to decide order of backups. More
7556 Timestamp in seconds is used to decide order of backups. More
7614 than ``maxbackups`` backups are kept, if same timestamp
7557 than ``maxbackups`` backups are kept, if same timestamp
7615 prevents from deciding exact order of them, for safety.
7558 prevents from deciding exact order of them, for safety.
7616
7559
7617 Selected changes can be unshelved with ``--interactive`` flag.
7560 Selected changes can be unshelved with ``--interactive`` flag.
7618 The working directory is updated with the selected changes, and
7561 The working directory is updated with the selected changes, and
7619 only the unselected changes remain shelved.
7562 only the unselected changes remain shelved.
7620 Note: The whole shelve is applied to working directory first before
7563 Note: The whole shelve is applied to working directory first before
7621 running interactively. So, this will bring up all the conflicts between
7564 running interactively. So, this will bring up all the conflicts between
7622 working directory and the shelve, irrespective of which changes will be
7565 working directory and the shelve, irrespective of which changes will be
7623 unshelved.
7566 unshelved.
7624 """
7567 """
7625 with repo.wlock():
7568 with repo.wlock():
7626 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7569 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7627
7570
7628
7571
7629 statemod.addunfinished(
7572 statemod.addunfinished(
7630 b'unshelve',
7573 b'unshelve',
7631 fname=b'shelvedstate',
7574 fname=b'shelvedstate',
7632 continueflag=True,
7575 continueflag=True,
7633 abortfunc=shelvemod.hgabortunshelve,
7576 abortfunc=shelvemod.hgabortunshelve,
7634 continuefunc=shelvemod.hgcontinueunshelve,
7577 continuefunc=shelvemod.hgcontinueunshelve,
7635 cmdmsg=_(b'unshelve already in progress'),
7578 cmdmsg=_(b'unshelve already in progress'),
7636 )
7579 )
7637
7580
7638
7581
7639 @command(
7582 @command(
7640 b'update|up|checkout|co',
7583 b'update|up|checkout|co',
7641 [
7584 [
7642 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7585 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7643 (b'c', b'check', None, _(b'require clean working directory')),
7586 (b'c', b'check', None, _(b'require clean working directory')),
7644 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7587 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7645 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7588 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7646 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7589 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7647 ]
7590 ]
7648 + mergetoolopts,
7591 + mergetoolopts,
7649 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7592 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7650 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7593 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7651 helpbasic=True,
7594 helpbasic=True,
7652 )
7595 )
7653 def update(ui, repo, node=None, **opts):
7596 def update(ui, repo, node=None, **opts):
7654 """update working directory (or switch revisions)
7597 """update working directory (or switch revisions)
7655
7598
7656 Update the repository's working directory to the specified
7599 Update the repository's working directory to the specified
7657 changeset. If no changeset is specified, update to the tip of the
7600 changeset. If no changeset is specified, update to the tip of the
7658 current named branch and move the active bookmark (see :hg:`help
7601 current named branch and move the active bookmark (see :hg:`help
7659 bookmarks`).
7602 bookmarks`).
7660
7603
7661 Update sets the working directory's parent revision to the specified
7604 Update sets the working directory's parent revision to the specified
7662 changeset (see :hg:`help parents`).
7605 changeset (see :hg:`help parents`).
7663
7606
7664 If the changeset is not a descendant or ancestor of the working
7607 If the changeset is not a descendant or ancestor of the working
7665 directory's parent and there are uncommitted changes, the update is
7608 directory's parent and there are uncommitted changes, the update is
7666 aborted. With the -c/--check option, the working directory is checked
7609 aborted. With the -c/--check option, the working directory is checked
7667 for uncommitted changes; if none are found, the working directory is
7610 for uncommitted changes; if none are found, the working directory is
7668 updated to the specified changeset.
7611 updated to the specified changeset.
7669
7612
7670 .. container:: verbose
7613 .. container:: verbose
7671
7614
7672 The -C/--clean, -c/--check, and -m/--merge options control what
7615 The -C/--clean, -c/--check, and -m/--merge options control what
7673 happens if the working directory contains uncommitted changes.
7616 happens if the working directory contains uncommitted changes.
7674 At most of one of them can be specified.
7617 At most of one of them can be specified.
7675
7618
7676 1. If no option is specified, and if
7619 1. If no option is specified, and if
7677 the requested changeset is an ancestor or descendant of
7620 the requested changeset is an ancestor or descendant of
7678 the working directory's parent, the uncommitted changes
7621 the working directory's parent, the uncommitted changes
7679 are merged into the requested changeset and the merged
7622 are merged into the requested changeset and the merged
7680 result is left uncommitted. If the requested changeset is
7623 result is left uncommitted. If the requested changeset is
7681 not an ancestor or descendant (that is, it is on another
7624 not an ancestor or descendant (that is, it is on another
7682 branch), the update is aborted and the uncommitted changes
7625 branch), the update is aborted and the uncommitted changes
7683 are preserved.
7626 are preserved.
7684
7627
7685 2. With the -m/--merge option, the update is allowed even if the
7628 2. With the -m/--merge option, the update is allowed even if the
7686 requested changeset is not an ancestor or descendant of
7629 requested changeset is not an ancestor or descendant of
7687 the working directory's parent.
7630 the working directory's parent.
7688
7631
7689 3. With the -c/--check option, the update is aborted and the
7632 3. With the -c/--check option, the update is aborted and the
7690 uncommitted changes are preserved.
7633 uncommitted changes are preserved.
7691
7634
7692 4. With the -C/--clean option, uncommitted changes are discarded and
7635 4. With the -C/--clean option, uncommitted changes are discarded and
7693 the working directory is updated to the requested changeset.
7636 the working directory is updated to the requested changeset.
7694
7637
7695 To cancel an uncommitted merge (and lose your changes), use
7638 To cancel an uncommitted merge (and lose your changes), use
7696 :hg:`merge --abort`.
7639 :hg:`merge --abort`.
7697
7640
7698 Use null as the changeset to remove the working directory (like
7641 Use null as the changeset to remove the working directory (like
7699 :hg:`clone -U`).
7642 :hg:`clone -U`).
7700
7643
7701 If you want to revert just one file to an older revision, use
7644 If you want to revert just one file to an older revision, use
7702 :hg:`revert [-r REV] NAME`.
7645 :hg:`revert [-r REV] NAME`.
7703
7646
7704 See :hg:`help dates` for a list of formats valid for -d/--date.
7647 See :hg:`help dates` for a list of formats valid for -d/--date.
7705
7648
7706 Returns 0 on success, 1 if there are unresolved files.
7649 Returns 0 on success, 1 if there are unresolved files.
7707 """
7650 """
7708 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7651 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7709 rev = opts.get('rev')
7652 rev = opts.get('rev')
7710 date = opts.get('date')
7653 date = opts.get('date')
7711 clean = opts.get('clean')
7654 clean = opts.get('clean')
7712 check = opts.get('check')
7655 check = opts.get('check')
7713 merge = opts.get('merge')
7656 merge = opts.get('merge')
7714 if rev and node:
7657 if rev and node:
7715 raise error.Abort(_(b"please specify just one revision"))
7658 raise error.Abort(_(b"please specify just one revision"))
7716
7659
7717 if ui.configbool(b'commands', b'update.requiredest'):
7660 if ui.configbool(b'commands', b'update.requiredest'):
7718 if not node and not rev and not date:
7661 if not node and not rev and not date:
7719 raise error.Abort(
7662 raise error.Abort(
7720 _(b'you must specify a destination'),
7663 _(b'you must specify a destination'),
7721 hint=_(b'for example: hg update ".::"'),
7664 hint=_(b'for example: hg update ".::"'),
7722 )
7665 )
7723
7666
7724 if rev is None or rev == b'':
7667 if rev is None or rev == b'':
7725 rev = node
7668 rev = node
7726
7669
7727 if date and rev is not None:
7670 if date and rev is not None:
7728 raise error.Abort(_(b"you can't specify a revision and a date"))
7671 raise error.Abort(_(b"you can't specify a revision and a date"))
7729
7672
7730 updatecheck = None
7673 updatecheck = None
7731 if check:
7674 if check:
7732 updatecheck = b'abort'
7675 updatecheck = b'abort'
7733 elif merge:
7676 elif merge:
7734 updatecheck = b'none'
7677 updatecheck = b'none'
7735
7678
7736 with repo.wlock():
7679 with repo.wlock():
7737 cmdutil.clearunfinished(repo)
7680 cmdutil.clearunfinished(repo)
7738 if date:
7681 if date:
7739 rev = cmdutil.finddate(ui, repo, date)
7682 rev = cmdutil.finddate(ui, repo, date)
7740
7683
7741 # if we defined a bookmark, we have to remember the original name
7684 # if we defined a bookmark, we have to remember the original name
7742 brev = rev
7685 brev = rev
7743 if rev:
7686 if rev:
7744 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7687 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7745 ctx = scmutil.revsingle(repo, rev, default=None)
7688 ctx = scmutil.revsingle(repo, rev, default=None)
7746 rev = ctx.rev()
7689 rev = ctx.rev()
7747 hidden = ctx.hidden()
7690 hidden = ctx.hidden()
7748 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7691 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7749 with ui.configoverride(overrides, b'update'):
7692 with ui.configoverride(overrides, b'update'):
7750 ret = hg.updatetotally(
7693 ret = hg.updatetotally(
7751 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7694 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7752 )
7695 )
7753 if hidden:
7696 if hidden:
7754 ctxstr = ctx.hex()[:12]
7697 ctxstr = ctx.hex()[:12]
7755 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7698 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7756
7699
7757 if ctx.obsolete():
7700 if ctx.obsolete():
7758 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7701 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7759 ui.warn(b"(%s)\n" % obsfatemsg)
7702 ui.warn(b"(%s)\n" % obsfatemsg)
7760 return ret
7703 return ret
7761
7704
7762
7705
7763 @command(
7706 @command(
7764 b'verify',
7707 b'verify',
7765 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7708 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7766 helpcategory=command.CATEGORY_MAINTENANCE,
7709 helpcategory=command.CATEGORY_MAINTENANCE,
7767 )
7710 )
7768 def verify(ui, repo, **opts):
7711 def verify(ui, repo, **opts):
7769 """verify the integrity of the repository
7712 """verify the integrity of the repository
7770
7713
7771 Verify the integrity of the current repository.
7714 Verify the integrity of the current repository.
7772
7715
7773 This will perform an extensive check of the repository's
7716 This will perform an extensive check of the repository's
7774 integrity, validating the hashes and checksums of each entry in
7717 integrity, validating the hashes and checksums of each entry in
7775 the changelog, manifest, and tracked files, as well as the
7718 the changelog, manifest, and tracked files, as well as the
7776 integrity of their crosslinks and indices.
7719 integrity of their crosslinks and indices.
7777
7720
7778 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7721 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7779 for more information about recovery from corruption of the
7722 for more information about recovery from corruption of the
7780 repository.
7723 repository.
7781
7724
7782 Returns 0 on success, 1 if errors are encountered.
7725 Returns 0 on success, 1 if errors are encountered.
7783 """
7726 """
7784 opts = pycompat.byteskwargs(opts)
7727 opts = pycompat.byteskwargs(opts)
7785
7728
7786 level = None
7729 level = None
7787 if opts[b'full']:
7730 if opts[b'full']:
7788 level = verifymod.VERIFY_FULL
7731 level = verifymod.VERIFY_FULL
7789 return hg.verify(repo, level)
7732 return hg.verify(repo, level)
7790
7733
7791
7734
7792 @command(
7735 @command(
7793 b'version',
7736 b'version',
7794 [] + formatteropts,
7737 [] + formatteropts,
7795 helpcategory=command.CATEGORY_HELP,
7738 helpcategory=command.CATEGORY_HELP,
7796 norepo=True,
7739 norepo=True,
7797 intents={INTENT_READONLY},
7740 intents={INTENT_READONLY},
7798 )
7741 )
7799 def version_(ui, **opts):
7742 def version_(ui, **opts):
7800 """output version and copyright information
7743 """output version and copyright information
7801
7744
7802 .. container:: verbose
7745 .. container:: verbose
7803
7746
7804 Template:
7747 Template:
7805
7748
7806 The following keywords are supported. See also :hg:`help templates`.
7749 The following keywords are supported. See also :hg:`help templates`.
7807
7750
7808 :extensions: List of extensions.
7751 :extensions: List of extensions.
7809 :ver: String. Version number.
7752 :ver: String. Version number.
7810
7753
7811 And each entry of ``{extensions}`` provides the following sub-keywords
7754 And each entry of ``{extensions}`` provides the following sub-keywords
7812 in addition to ``{ver}``.
7755 in addition to ``{ver}``.
7813
7756
7814 :bundled: Boolean. True if included in the release.
7757 :bundled: Boolean. True if included in the release.
7815 :name: String. Extension name.
7758 :name: String. Extension name.
7816 """
7759 """
7817 opts = pycompat.byteskwargs(opts)
7760 opts = pycompat.byteskwargs(opts)
7818 if ui.verbose:
7761 if ui.verbose:
7819 ui.pager(b'version')
7762 ui.pager(b'version')
7820 fm = ui.formatter(b"version", opts)
7763 fm = ui.formatter(b"version", opts)
7821 fm.startitem()
7764 fm.startitem()
7822 fm.write(
7765 fm.write(
7823 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7766 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7824 )
7767 )
7825 license = _(
7768 license = _(
7826 b"(see https://mercurial-scm.org for more information)\n"
7769 b"(see https://mercurial-scm.org for more information)\n"
7827 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7770 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7828 b"This is free software; see the source for copying conditions. "
7771 b"This is free software; see the source for copying conditions. "
7829 b"There is NO\nwarranty; "
7772 b"There is NO\nwarranty; "
7830 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7773 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7831 )
7774 )
7832 if not ui.quiet:
7775 if not ui.quiet:
7833 fm.plain(license)
7776 fm.plain(license)
7834
7777
7835 if ui.verbose:
7778 if ui.verbose:
7836 fm.plain(_(b"\nEnabled extensions:\n\n"))
7779 fm.plain(_(b"\nEnabled extensions:\n\n"))
7837 # format names and versions into columns
7780 # format names and versions into columns
7838 names = []
7781 names = []
7839 vers = []
7782 vers = []
7840 isinternals = []
7783 isinternals = []
7841 for name, module in sorted(extensions.extensions()):
7784 for name, module in sorted(extensions.extensions()):
7842 names.append(name)
7785 names.append(name)
7843 vers.append(extensions.moduleversion(module) or None)
7786 vers.append(extensions.moduleversion(module) or None)
7844 isinternals.append(extensions.ismoduleinternal(module))
7787 isinternals.append(extensions.ismoduleinternal(module))
7845 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7788 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7846 if names:
7789 if names:
7847 namefmt = b" %%-%ds " % max(len(n) for n in names)
7790 namefmt = b" %%-%ds " % max(len(n) for n in names)
7848 places = [_(b"external"), _(b"internal")]
7791 places = [_(b"external"), _(b"internal")]
7849 for n, v, p in zip(names, vers, isinternals):
7792 for n, v, p in zip(names, vers, isinternals):
7850 fn.startitem()
7793 fn.startitem()
7851 fn.condwrite(ui.verbose, b"name", namefmt, n)
7794 fn.condwrite(ui.verbose, b"name", namefmt, n)
7852 if ui.verbose:
7795 if ui.verbose:
7853 fn.plain(b"%s " % places[p])
7796 fn.plain(b"%s " % places[p])
7854 fn.data(bundled=p)
7797 fn.data(bundled=p)
7855 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7798 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7856 if ui.verbose:
7799 if ui.verbose:
7857 fn.plain(b"\n")
7800 fn.plain(b"\n")
7858 fn.end()
7801 fn.end()
7859 fm.end()
7802 fm.end()
7860
7803
7861
7804
7862 def loadcmdtable(ui, name, cmdtable):
7805 def loadcmdtable(ui, name, cmdtable):
7863 """Load command functions from specified cmdtable
7806 """Load command functions from specified cmdtable
7864 """
7807 """
7865 overrides = [cmd for cmd in cmdtable if cmd in table]
7808 overrides = [cmd for cmd in cmdtable if cmd in table]
7866 if overrides:
7809 if overrides:
7867 ui.warn(
7810 ui.warn(
7868 _(b"extension '%s' overrides commands: %s\n")
7811 _(b"extension '%s' overrides commands: %s\n")
7869 % (name, b" ".join(overrides))
7812 % (name, b" ".join(overrides))
7870 )
7813 )
7871 table.update(cmdtable)
7814 table.update(cmdtable)
@@ -1,1591 +1,1588 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,
207 b'cmdserver', b'message-encodings', default=list,
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'cmdserver', b'shutdown-on-interrupt', default=True,
215 b'cmdserver', b'shutdown-on-interrupt', default=True,
216 )
216 )
217 coreconfigitem(
217 coreconfigitem(
218 b'color', b'.*', default=None, generic=True,
218 b'color', b'.*', default=None, generic=True,
219 )
219 )
220 coreconfigitem(
220 coreconfigitem(
221 b'color', b'mode', default=b'auto',
221 b'color', b'mode', default=b'auto',
222 )
222 )
223 coreconfigitem(
223 coreconfigitem(
224 b'color', b'pagermode', default=dynamicdefault,
224 b'color', b'pagermode', default=dynamicdefault,
225 )
225 )
226 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
226 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
227 coreconfigitem(
227 coreconfigitem(
228 b'commands', b'commit.post-status', default=False,
228 b'commands', b'commit.post-status', default=False,
229 )
229 )
230 coreconfigitem(
230 coreconfigitem(
231 b'commands', b'grep.all-files', default=False, experimental=True,
231 b'commands', b'grep.all-files', default=False, experimental=True,
232 )
232 )
233 coreconfigitem(
233 coreconfigitem(
234 b'commands', b'merge.require-rev', default=False,
234 b'commands', b'merge.require-rev', default=False,
235 )
235 )
236 coreconfigitem(
236 coreconfigitem(
237 b'commands', b'push.require-revs', default=False,
237 b'commands', b'push.require-revs', default=False,
238 )
238 )
239 coreconfigitem(
239 coreconfigitem(
240 b'commands', b'resolve.confirm', default=False,
240 b'commands', b'resolve.confirm', default=False,
241 )
241 )
242 coreconfigitem(
242 coreconfigitem(
243 b'commands', b'resolve.explicit-re-merge', default=False,
243 b'commands', b'resolve.explicit-re-merge', default=False,
244 )
244 )
245 coreconfigitem(
245 coreconfigitem(
246 b'commands', b'resolve.mark-check', default=b'none',
246 b'commands', b'resolve.mark-check', default=b'none',
247 )
247 )
248 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
248 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
249 coreconfigitem(
249 coreconfigitem(
250 b'commands', b'show.aliasprefix', default=list,
250 b'commands', b'show.aliasprefix', default=list,
251 )
251 )
252 coreconfigitem(
252 coreconfigitem(
253 b'commands', b'status.relative', default=False,
253 b'commands', b'status.relative', default=False,
254 )
254 )
255 coreconfigitem(
255 coreconfigitem(
256 b'commands', b'status.skipstates', default=[], experimental=True,
256 b'commands', b'status.skipstates', default=[], experimental=True,
257 )
257 )
258 coreconfigitem(
258 coreconfigitem(
259 b'commands', b'status.terse', default=b'',
259 b'commands', b'status.terse', default=b'',
260 )
260 )
261 coreconfigitem(
261 coreconfigitem(
262 b'commands', b'status.verbose', default=False,
262 b'commands', b'status.verbose', default=False,
263 )
263 )
264 coreconfigitem(
264 coreconfigitem(
265 b'commands', b'update.check', default=None,
265 b'commands', b'update.check', default=None,
266 )
266 )
267 coreconfigitem(
267 coreconfigitem(
268 b'commands', b'update.requiredest', default=False,
268 b'commands', b'update.requiredest', default=False,
269 )
269 )
270 coreconfigitem(
270 coreconfigitem(
271 b'committemplate', b'.*', default=None, generic=True,
271 b'committemplate', b'.*', default=None, generic=True,
272 )
272 )
273 coreconfigitem(
273 coreconfigitem(
274 b'convert', b'bzr.saverev', default=True,
274 b'convert', b'bzr.saverev', default=True,
275 )
275 )
276 coreconfigitem(
276 coreconfigitem(
277 b'convert', b'cvsps.cache', default=True,
277 b'convert', b'cvsps.cache', default=True,
278 )
278 )
279 coreconfigitem(
279 coreconfigitem(
280 b'convert', b'cvsps.fuzz', default=60,
280 b'convert', b'cvsps.fuzz', default=60,
281 )
281 )
282 coreconfigitem(
282 coreconfigitem(
283 b'convert', b'cvsps.logencoding', default=None,
283 b'convert', b'cvsps.logencoding', default=None,
284 )
284 )
285 coreconfigitem(
285 coreconfigitem(
286 b'convert', b'cvsps.mergefrom', default=None,
286 b'convert', b'cvsps.mergefrom', default=None,
287 )
287 )
288 coreconfigitem(
288 coreconfigitem(
289 b'convert', b'cvsps.mergeto', default=None,
289 b'convert', b'cvsps.mergeto', default=None,
290 )
290 )
291 coreconfigitem(
291 coreconfigitem(
292 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
292 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
293 )
293 )
294 coreconfigitem(
294 coreconfigitem(
295 b'convert', b'git.extrakeys', default=list,
295 b'convert', b'git.extrakeys', default=list,
296 )
296 )
297 coreconfigitem(
297 coreconfigitem(
298 b'convert', b'git.findcopiesharder', default=False,
298 b'convert', b'git.findcopiesharder', default=False,
299 )
299 )
300 coreconfigitem(
300 coreconfigitem(
301 b'convert', b'git.remoteprefix', default=b'remote',
301 b'convert', b'git.remoteprefix', default=b'remote',
302 )
302 )
303 coreconfigitem(
303 coreconfigitem(
304 b'convert', b'git.renamelimit', default=400,
304 b'convert', b'git.renamelimit', default=400,
305 )
305 )
306 coreconfigitem(
306 coreconfigitem(
307 b'convert', b'git.saverev', default=True,
307 b'convert', b'git.saverev', default=True,
308 )
308 )
309 coreconfigitem(
309 coreconfigitem(
310 b'convert', b'git.similarity', default=50,
310 b'convert', b'git.similarity', default=50,
311 )
311 )
312 coreconfigitem(
312 coreconfigitem(
313 b'convert', b'git.skipsubmodules', default=False,
313 b'convert', b'git.skipsubmodules', default=False,
314 )
314 )
315 coreconfigitem(
315 coreconfigitem(
316 b'convert', b'hg.clonebranches', default=False,
316 b'convert', b'hg.clonebranches', default=False,
317 )
317 )
318 coreconfigitem(
318 coreconfigitem(
319 b'convert', b'hg.ignoreerrors', default=False,
319 b'convert', b'hg.ignoreerrors', default=False,
320 )
320 )
321 coreconfigitem(
321 coreconfigitem(
322 b'convert', b'hg.preserve-hash', default=False,
322 b'convert', b'hg.preserve-hash', default=False,
323 )
323 )
324 coreconfigitem(
324 coreconfigitem(
325 b'convert', b'hg.revs', default=None,
325 b'convert', b'hg.revs', default=None,
326 )
326 )
327 coreconfigitem(
327 coreconfigitem(
328 b'convert', b'hg.saverev', default=False,
328 b'convert', b'hg.saverev', default=False,
329 )
329 )
330 coreconfigitem(
330 coreconfigitem(
331 b'convert', b'hg.sourcename', default=None,
331 b'convert', b'hg.sourcename', default=None,
332 )
332 )
333 coreconfigitem(
333 coreconfigitem(
334 b'convert', b'hg.startrev', default=None,
334 b'convert', b'hg.startrev', default=None,
335 )
335 )
336 coreconfigitem(
336 coreconfigitem(
337 b'convert', b'hg.tagsbranch', default=b'default',
337 b'convert', b'hg.tagsbranch', default=b'default',
338 )
338 )
339 coreconfigitem(
339 coreconfigitem(
340 b'convert', b'hg.usebranchnames', default=True,
340 b'convert', b'hg.usebranchnames', default=True,
341 )
341 )
342 coreconfigitem(
342 coreconfigitem(
343 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
343 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
344 )
344 )
345 coreconfigitem(
345 coreconfigitem(
346 b'convert', b'localtimezone', default=False,
346 b'convert', b'localtimezone', default=False,
347 )
347 )
348 coreconfigitem(
348 coreconfigitem(
349 b'convert', b'p4.encoding', default=dynamicdefault,
349 b'convert', b'p4.encoding', default=dynamicdefault,
350 )
350 )
351 coreconfigitem(
351 coreconfigitem(
352 b'convert', b'p4.startrev', default=0,
352 b'convert', b'p4.startrev', default=0,
353 )
353 )
354 coreconfigitem(
354 coreconfigitem(
355 b'convert', b'skiptags', default=False,
355 b'convert', b'skiptags', default=False,
356 )
356 )
357 coreconfigitem(
357 coreconfigitem(
358 b'convert', b'svn.debugsvnlog', default=True,
358 b'convert', b'svn.debugsvnlog', default=True,
359 )
359 )
360 coreconfigitem(
360 coreconfigitem(
361 b'convert', b'svn.trunk', default=None,
361 b'convert', b'svn.trunk', default=None,
362 )
362 )
363 coreconfigitem(
363 coreconfigitem(
364 b'convert', b'svn.tags', default=None,
364 b'convert', b'svn.tags', default=None,
365 )
365 )
366 coreconfigitem(
366 coreconfigitem(
367 b'convert', b'svn.branches', default=None,
367 b'convert', b'svn.branches', default=None,
368 )
368 )
369 coreconfigitem(
369 coreconfigitem(
370 b'convert', b'svn.startrev', default=0,
370 b'convert', b'svn.startrev', default=0,
371 )
371 )
372 coreconfigitem(
372 coreconfigitem(
373 b'debug', b'dirstate.delaywrite', default=0,
373 b'debug', b'dirstate.delaywrite', default=0,
374 )
374 )
375 coreconfigitem(
375 coreconfigitem(
376 b'defaults', b'.*', default=None, generic=True,
376 b'defaults', b'.*', default=None, generic=True,
377 )
377 )
378 coreconfigitem(
378 coreconfigitem(
379 b'devel', b'all-warnings', default=False,
379 b'devel', b'all-warnings', default=False,
380 )
380 )
381 coreconfigitem(
381 coreconfigitem(
382 b'devel', b'bundle2.debug', default=False,
382 b'devel', b'bundle2.debug', default=False,
383 )
383 )
384 coreconfigitem(
384 coreconfigitem(
385 b'devel', b'bundle.delta', default=b'',
385 b'devel', b'bundle.delta', default=b'',
386 )
386 )
387 coreconfigitem(
387 coreconfigitem(
388 b'devel', b'cache-vfs', default=None,
388 b'devel', b'cache-vfs', default=None,
389 )
389 )
390 coreconfigitem(
390 coreconfigitem(
391 b'devel', b'check-locks', default=False,
391 b'devel', b'check-locks', default=False,
392 )
392 )
393 coreconfigitem(
393 coreconfigitem(
394 b'devel', b'check-relroot', default=False,
394 b'devel', b'check-relroot', default=False,
395 )
395 )
396 coreconfigitem(
396 coreconfigitem(
397 b'devel', b'default-date', default=None,
397 b'devel', b'default-date', default=None,
398 )
398 )
399 coreconfigitem(
399 coreconfigitem(
400 b'devel', b'deprec-warn', default=False,
400 b'devel', b'deprec-warn', default=False,
401 )
401 )
402 coreconfigitem(
402 coreconfigitem(
403 b'devel', b'disableloaddefaultcerts', default=False,
403 b'devel', b'disableloaddefaultcerts', default=False,
404 )
404 )
405 coreconfigitem(
405 coreconfigitem(
406 b'devel', b'warn-empty-changegroup', default=False,
406 b'devel', b'warn-empty-changegroup', default=False,
407 )
407 )
408 coreconfigitem(
408 coreconfigitem(
409 b'devel', b'legacy.exchange', default=list,
409 b'devel', b'legacy.exchange', default=list,
410 )
410 )
411 coreconfigitem(
411 coreconfigitem(
412 b'devel', b'persistent-nodemap', default=False,
412 b'devel', b'persistent-nodemap', default=False,
413 )
413 )
414 coreconfigitem(
414 coreconfigitem(
415 b'devel', b'servercafile', default=b'',
415 b'devel', b'servercafile', default=b'',
416 )
416 )
417 coreconfigitem(
417 coreconfigitem(
418 b'devel', b'serverexactprotocol', default=b'',
418 b'devel', b'serverexactprotocol', default=b'',
419 )
419 )
420 coreconfigitem(
420 coreconfigitem(
421 b'devel', b'serverrequirecert', default=False,
421 b'devel', b'serverrequirecert', default=False,
422 )
422 )
423 coreconfigitem(
423 coreconfigitem(
424 b'devel', b'strip-obsmarkers', default=True,
424 b'devel', b'strip-obsmarkers', default=True,
425 )
425 )
426 coreconfigitem(
426 coreconfigitem(
427 b'devel', b'warn-config', default=None,
427 b'devel', b'warn-config', default=None,
428 )
428 )
429 coreconfigitem(
429 coreconfigitem(
430 b'devel', b'warn-config-default', default=None,
430 b'devel', b'warn-config-default', default=None,
431 )
431 )
432 coreconfigitem(
432 coreconfigitem(
433 b'devel', b'user.obsmarker', default=None,
433 b'devel', b'user.obsmarker', default=None,
434 )
434 )
435 coreconfigitem(
435 coreconfigitem(
436 b'devel', b'warn-config-unknown', default=None,
436 b'devel', b'warn-config-unknown', default=None,
437 )
437 )
438 coreconfigitem(
438 coreconfigitem(
439 b'devel', b'debug.copies', default=False,
439 b'devel', b'debug.copies', default=False,
440 )
440 )
441 coreconfigitem(
441 coreconfigitem(
442 b'devel', b'debug.extensions', default=False,
442 b'devel', b'debug.extensions', default=False,
443 )
443 )
444 coreconfigitem(
444 coreconfigitem(
445 b'devel', b'debug.repo-filters', default=False,
445 b'devel', b'debug.repo-filters', default=False,
446 )
446 )
447 coreconfigitem(
447 coreconfigitem(
448 b'devel', b'debug.peer-request', default=False,
448 b'devel', b'debug.peer-request', default=False,
449 )
449 )
450 coreconfigitem(
450 coreconfigitem(
451 b'devel', b'discovery.randomize', default=True,
451 b'devel', b'discovery.randomize', default=True,
452 )
452 )
453 _registerdiffopts(section=b'diff')
453 _registerdiffopts(section=b'diff')
454 coreconfigitem(
454 coreconfigitem(
455 b'email', b'bcc', default=None,
455 b'email', b'bcc', default=None,
456 )
456 )
457 coreconfigitem(
457 coreconfigitem(
458 b'email', b'cc', default=None,
458 b'email', b'cc', default=None,
459 )
459 )
460 coreconfigitem(
460 coreconfigitem(
461 b'email', b'charsets', default=list,
461 b'email', b'charsets', default=list,
462 )
462 )
463 coreconfigitem(
463 coreconfigitem(
464 b'email', b'from', default=None,
464 b'email', b'from', default=None,
465 )
465 )
466 coreconfigitem(
466 coreconfigitem(
467 b'email', b'method', default=b'smtp',
467 b'email', b'method', default=b'smtp',
468 )
468 )
469 coreconfigitem(
469 coreconfigitem(
470 b'email', b'reply-to', default=None,
470 b'email', b'reply-to', default=None,
471 )
471 )
472 coreconfigitem(
472 coreconfigitem(
473 b'email', b'to', default=None,
473 b'email', b'to', default=None,
474 )
474 )
475 coreconfigitem(
475 coreconfigitem(
476 b'experimental', b'archivemetatemplate', default=dynamicdefault,
476 b'experimental', b'archivemetatemplate', default=dynamicdefault,
477 )
477 )
478 coreconfigitem(
478 coreconfigitem(
479 b'experimental', b'auto-publish', default=b'publish',
479 b'experimental', b'auto-publish', default=b'publish',
480 )
480 )
481 coreconfigitem(
481 coreconfigitem(
482 b'experimental', b'bundle-phases', default=False,
482 b'experimental', b'bundle-phases', default=False,
483 )
483 )
484 coreconfigitem(
484 coreconfigitem(
485 b'experimental', b'bundle2-advertise', default=True,
485 b'experimental', b'bundle2-advertise', default=True,
486 )
486 )
487 coreconfigitem(
487 coreconfigitem(
488 b'experimental', b'bundle2-output-capture', default=False,
488 b'experimental', b'bundle2-output-capture', default=False,
489 )
489 )
490 coreconfigitem(
490 coreconfigitem(
491 b'experimental', b'bundle2.pushback', default=False,
491 b'experimental', b'bundle2.pushback', default=False,
492 )
492 )
493 coreconfigitem(
493 coreconfigitem(
494 b'experimental', b'bundle2lazylocking', default=False,
494 b'experimental', b'bundle2lazylocking', default=False,
495 )
495 )
496 coreconfigitem(
496 coreconfigitem(
497 b'experimental', b'bundlecomplevel', default=None,
497 b'experimental', b'bundlecomplevel', default=None,
498 )
498 )
499 coreconfigitem(
499 coreconfigitem(
500 b'experimental', b'bundlecomplevel.bzip2', default=None,
500 b'experimental', b'bundlecomplevel.bzip2', default=None,
501 )
501 )
502 coreconfigitem(
502 coreconfigitem(
503 b'experimental', b'bundlecomplevel.gzip', default=None,
503 b'experimental', b'bundlecomplevel.gzip', default=None,
504 )
504 )
505 coreconfigitem(
505 coreconfigitem(
506 b'experimental', b'bundlecomplevel.none', default=None,
506 b'experimental', b'bundlecomplevel.none', default=None,
507 )
507 )
508 coreconfigitem(
508 coreconfigitem(
509 b'experimental', b'bundlecomplevel.zstd', default=None,
509 b'experimental', b'bundlecomplevel.zstd', default=None,
510 )
510 )
511 coreconfigitem(
511 coreconfigitem(
512 b'experimental', b'changegroup3', default=False,
512 b'experimental', b'changegroup3', default=False,
513 )
513 )
514 coreconfigitem(
514 coreconfigitem(
515 b'experimental', b'cleanup-as-archived', default=False,
515 b'experimental', b'cleanup-as-archived', default=False,
516 )
516 )
517 coreconfigitem(
517 coreconfigitem(
518 b'experimental', b'clientcompressionengines', default=list,
518 b'experimental', b'clientcompressionengines', default=list,
519 )
519 )
520 coreconfigitem(
520 coreconfigitem(
521 b'experimental', b'copytrace', default=b'on',
521 b'experimental', b'copytrace', default=b'on',
522 )
522 )
523 coreconfigitem(
523 coreconfigitem(
524 b'experimental', b'copytrace.movecandidateslimit', default=100,
524 b'experimental', b'copytrace.movecandidateslimit', default=100,
525 )
525 )
526 coreconfigitem(
526 coreconfigitem(
527 b'experimental', b'copytrace.sourcecommitlimit', default=100,
527 b'experimental', b'copytrace.sourcecommitlimit', default=100,
528 )
528 )
529 coreconfigitem(
529 coreconfigitem(
530 b'experimental', b'copies.read-from', default=b"filelog-only",
530 b'experimental', b'copies.read-from', default=b"filelog-only",
531 )
531 )
532 coreconfigitem(
532 coreconfigitem(
533 b'experimental', b'copies.write-to', default=b'filelog-only',
533 b'experimental', b'copies.write-to', default=b'filelog-only',
534 )
534 )
535 coreconfigitem(
535 coreconfigitem(
536 b'experimental', b'crecordtest', default=None,
536 b'experimental', b'crecordtest', default=None,
537 )
537 )
538 coreconfigitem(
538 coreconfigitem(
539 b'experimental', b'directaccess', default=False,
539 b'experimental', b'directaccess', default=False,
540 )
540 )
541 coreconfigitem(
541 coreconfigitem(
542 b'experimental', b'directaccess.revnums', default=False,
542 b'experimental', b'directaccess.revnums', default=False,
543 )
543 )
544 coreconfigitem(
544 coreconfigitem(
545 b'experimental', b'editortmpinhg', default=False,
545 b'experimental', b'editortmpinhg', default=False,
546 )
546 )
547 coreconfigitem(
547 coreconfigitem(
548 b'experimental', b'evolution', default=list,
548 b'experimental', b'evolution', default=list,
549 )
549 )
550 coreconfigitem(
550 coreconfigitem(
551 b'experimental',
551 b'experimental',
552 b'evolution.allowdivergence',
552 b'evolution.allowdivergence',
553 default=False,
553 default=False,
554 alias=[(b'experimental', b'allowdivergence')],
554 alias=[(b'experimental', b'allowdivergence')],
555 )
555 )
556 coreconfigitem(
556 coreconfigitem(
557 b'experimental', b'evolution.allowunstable', default=None,
557 b'experimental', b'evolution.allowunstable', default=None,
558 )
558 )
559 coreconfigitem(
559 coreconfigitem(
560 b'experimental', b'evolution.createmarkers', default=None,
560 b'experimental', b'evolution.createmarkers', default=None,
561 )
561 )
562 coreconfigitem(
562 coreconfigitem(
563 b'experimental',
563 b'experimental',
564 b'evolution.effect-flags',
564 b'evolution.effect-flags',
565 default=True,
565 default=True,
566 alias=[(b'experimental', b'effect-flags')],
566 alias=[(b'experimental', b'effect-flags')],
567 )
567 )
568 coreconfigitem(
568 coreconfigitem(
569 b'experimental', b'evolution.exchange', default=None,
569 b'experimental', b'evolution.exchange', default=None,
570 )
570 )
571 coreconfigitem(
571 coreconfigitem(
572 b'experimental', b'evolution.bundle-obsmarker', default=False,
572 b'experimental', b'evolution.bundle-obsmarker', default=False,
573 )
573 )
574 coreconfigitem(
574 coreconfigitem(
575 b'experimental', b'log.topo', default=False,
575 b'experimental', b'log.topo', default=False,
576 )
576 )
577 coreconfigitem(
577 coreconfigitem(
578 b'experimental', b'evolution.report-instabilities', default=True,
578 b'experimental', b'evolution.report-instabilities', default=True,
579 )
579 )
580 coreconfigitem(
580 coreconfigitem(
581 b'experimental', b'evolution.track-operation', default=True,
581 b'experimental', b'evolution.track-operation', default=True,
582 )
582 )
583 # repo-level config to exclude a revset visibility
583 # repo-level config to exclude a revset visibility
584 #
584 #
585 # The target use case is to use `share` to expose different subset of the same
585 # The target use case is to use `share` to expose different subset of the same
586 # repository, especially server side. See also `server.view`.
586 # repository, especially server side. See also `server.view`.
587 coreconfigitem(
587 coreconfigitem(
588 b'experimental', b'extra-filter-revs', default=None,
588 b'experimental', b'extra-filter-revs', default=None,
589 )
589 )
590 coreconfigitem(
590 coreconfigitem(
591 b'experimental', b'maxdeltachainspan', default=-1,
591 b'experimental', b'maxdeltachainspan', default=-1,
592 )
592 )
593 coreconfigitem(
593 coreconfigitem(
594 b'experimental', b'mergetempdirprefix', default=None,
594 b'experimental', b'mergetempdirprefix', default=None,
595 )
595 )
596 coreconfigitem(
596 coreconfigitem(
597 b'experimental', b'mmapindexthreshold', default=None,
597 b'experimental', b'mmapindexthreshold', default=None,
598 )
598 )
599 coreconfigitem(
599 coreconfigitem(
600 b'experimental', b'narrow', default=False,
600 b'experimental', b'narrow', default=False,
601 )
601 )
602 coreconfigitem(
602 coreconfigitem(
603 b'experimental', b'nonnormalparanoidcheck', default=False,
603 b'experimental', b'nonnormalparanoidcheck', default=False,
604 )
604 )
605 coreconfigitem(
605 coreconfigitem(
606 b'experimental', b'exportableenviron', default=list,
606 b'experimental', b'exportableenviron', default=list,
607 )
607 )
608 coreconfigitem(
608 coreconfigitem(
609 b'experimental', b'extendedheader.index', default=None,
609 b'experimental', b'extendedheader.index', default=None,
610 )
610 )
611 coreconfigitem(
611 coreconfigitem(
612 b'experimental', b'extendedheader.similarity', default=False,
612 b'experimental', b'extendedheader.similarity', default=False,
613 )
613 )
614 coreconfigitem(
614 coreconfigitem(
615 b'experimental', b'graphshorten', default=False,
615 b'experimental', b'graphshorten', default=False,
616 )
616 )
617 coreconfigitem(
617 coreconfigitem(
618 b'experimental', b'graphstyle.parent', default=dynamicdefault,
618 b'experimental', b'graphstyle.parent', default=dynamicdefault,
619 )
619 )
620 coreconfigitem(
620 coreconfigitem(
621 b'experimental', b'graphstyle.missing', default=dynamicdefault,
621 b'experimental', b'graphstyle.missing', default=dynamicdefault,
622 )
622 )
623 coreconfigitem(
623 coreconfigitem(
624 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
624 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
625 )
625 )
626 coreconfigitem(
626 coreconfigitem(
627 b'experimental', b'hook-track-tags', default=False,
627 b'experimental', b'hook-track-tags', default=False,
628 )
628 )
629 coreconfigitem(
629 coreconfigitem(
630 b'experimental', b'httppeer.advertise-v2', default=False,
630 b'experimental', b'httppeer.advertise-v2', default=False,
631 )
631 )
632 coreconfigitem(
632 coreconfigitem(
633 b'experimental', b'httppeer.v2-encoder-order', default=None,
633 b'experimental', b'httppeer.v2-encoder-order', default=None,
634 )
634 )
635 coreconfigitem(
635 coreconfigitem(
636 b'experimental', b'httppostargs', default=False,
636 b'experimental', b'httppostargs', default=False,
637 )
637 )
638 coreconfigitem(
639 b'experimental', b'mergedriver', default=None,
640 )
641 coreconfigitem(b'experimental', b'nointerrupt', default=False)
638 coreconfigitem(b'experimental', b'nointerrupt', default=False)
642 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
639 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
643
640
644 coreconfigitem(
641 coreconfigitem(
645 b'experimental', b'obsmarkers-exchange-debug', default=False,
642 b'experimental', b'obsmarkers-exchange-debug', default=False,
646 )
643 )
647 coreconfigitem(
644 coreconfigitem(
648 b'experimental', b'remotenames', default=False,
645 b'experimental', b'remotenames', default=False,
649 )
646 )
650 coreconfigitem(
647 coreconfigitem(
651 b'experimental', b'removeemptydirs', default=True,
648 b'experimental', b'removeemptydirs', default=True,
652 )
649 )
653 coreconfigitem(
650 coreconfigitem(
654 b'experimental', b'revert.interactive.select-to-keep', default=False,
651 b'experimental', b'revert.interactive.select-to-keep', default=False,
655 )
652 )
656 coreconfigitem(
653 coreconfigitem(
657 b'experimental', b'revisions.prefixhexnode', default=False,
654 b'experimental', b'revisions.prefixhexnode', default=False,
658 )
655 )
659 coreconfigitem(
656 coreconfigitem(
660 b'experimental', b'revlogv2', default=None,
657 b'experimental', b'revlogv2', default=None,
661 )
658 )
662 coreconfigitem(
659 coreconfigitem(
663 b'experimental', b'revisions.disambiguatewithin', default=None,
660 b'experimental', b'revisions.disambiguatewithin', default=None,
664 )
661 )
665 coreconfigitem(
662 coreconfigitem(
666 b'experimental', b'rust.index', default=False,
663 b'experimental', b'rust.index', default=False,
667 )
664 )
668 coreconfigitem(
665 coreconfigitem(
669 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
666 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
670 )
667 )
671 coreconfigitem(
668 coreconfigitem(
672 b'experimental',
669 b'experimental',
673 b'server.manifestdata.recommended-batch-size',
670 b'server.manifestdata.recommended-batch-size',
674 default=100000,
671 default=100000,
675 )
672 )
676 coreconfigitem(
673 coreconfigitem(
677 b'experimental', b'server.stream-narrow-clones', default=False,
674 b'experimental', b'server.stream-narrow-clones', default=False,
678 )
675 )
679 coreconfigitem(
676 coreconfigitem(
680 b'experimental', b'single-head-per-branch', default=False,
677 b'experimental', b'single-head-per-branch', default=False,
681 )
678 )
682 coreconfigitem(
679 coreconfigitem(
683 b'experimental',
680 b'experimental',
684 b'single-head-per-branch:account-closed-heads',
681 b'single-head-per-branch:account-closed-heads',
685 default=False,
682 default=False,
686 )
683 )
687 coreconfigitem(
684 coreconfigitem(
688 b'experimental', b'sshserver.support-v2', default=False,
685 b'experimental', b'sshserver.support-v2', default=False,
689 )
686 )
690 coreconfigitem(
687 coreconfigitem(
691 b'experimental', b'sparse-read', default=False,
688 b'experimental', b'sparse-read', default=False,
692 )
689 )
693 coreconfigitem(
690 coreconfigitem(
694 b'experimental', b'sparse-read.density-threshold', default=0.50,
691 b'experimental', b'sparse-read.density-threshold', default=0.50,
695 )
692 )
696 coreconfigitem(
693 coreconfigitem(
697 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
694 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
698 )
695 )
699 coreconfigitem(
696 coreconfigitem(
700 b'experimental', b'treemanifest', default=False,
697 b'experimental', b'treemanifest', default=False,
701 )
698 )
702 coreconfigitem(
699 coreconfigitem(
703 b'experimental', b'update.atomic-file', default=False,
700 b'experimental', b'update.atomic-file', default=False,
704 )
701 )
705 coreconfigitem(
702 coreconfigitem(
706 b'experimental', b'sshpeer.advertise-v2', default=False,
703 b'experimental', b'sshpeer.advertise-v2', default=False,
707 )
704 )
708 coreconfigitem(
705 coreconfigitem(
709 b'experimental', b'web.apiserver', default=False,
706 b'experimental', b'web.apiserver', default=False,
710 )
707 )
711 coreconfigitem(
708 coreconfigitem(
712 b'experimental', b'web.api.http-v2', default=False,
709 b'experimental', b'web.api.http-v2', default=False,
713 )
710 )
714 coreconfigitem(
711 coreconfigitem(
715 b'experimental', b'web.api.debugreflect', default=False,
712 b'experimental', b'web.api.debugreflect', default=False,
716 )
713 )
717 coreconfigitem(
714 coreconfigitem(
718 b'experimental', b'worker.wdir-get-thread-safe', default=False,
715 b'experimental', b'worker.wdir-get-thread-safe', default=False,
719 )
716 )
720 coreconfigitem(
717 coreconfigitem(
721 b'experimental', b'worker.repository-upgrade', default=False,
718 b'experimental', b'worker.repository-upgrade', default=False,
722 )
719 )
723 coreconfigitem(
720 coreconfigitem(
724 b'experimental', b'xdiff', default=False,
721 b'experimental', b'xdiff', default=False,
725 )
722 )
726 coreconfigitem(
723 coreconfigitem(
727 b'extensions', b'.*', default=None, generic=True,
724 b'extensions', b'.*', default=None, generic=True,
728 )
725 )
729 coreconfigitem(
726 coreconfigitem(
730 b'extdata', b'.*', default=None, generic=True,
727 b'extdata', b'.*', default=None, generic=True,
731 )
728 )
732 coreconfigitem(
729 coreconfigitem(
733 b'format', b'bookmarks-in-store', default=False,
730 b'format', b'bookmarks-in-store', default=False,
734 )
731 )
735 coreconfigitem(
732 coreconfigitem(
736 b'format', b'chunkcachesize', default=None, experimental=True,
733 b'format', b'chunkcachesize', default=None, experimental=True,
737 )
734 )
738 coreconfigitem(
735 coreconfigitem(
739 b'format', b'dotencode', default=True,
736 b'format', b'dotencode', default=True,
740 )
737 )
741 coreconfigitem(
738 coreconfigitem(
742 b'format', b'generaldelta', default=False, experimental=True,
739 b'format', b'generaldelta', default=False, experimental=True,
743 )
740 )
744 coreconfigitem(
741 coreconfigitem(
745 b'format', b'manifestcachesize', default=None, experimental=True,
742 b'format', b'manifestcachesize', default=None, experimental=True,
746 )
743 )
747 coreconfigitem(
744 coreconfigitem(
748 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
745 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
749 )
746 )
750 coreconfigitem(
747 coreconfigitem(
751 b'format', b'obsstore-version', default=None,
748 b'format', b'obsstore-version', default=None,
752 )
749 )
753 coreconfigitem(
750 coreconfigitem(
754 b'format', b'sparse-revlog', default=True,
751 b'format', b'sparse-revlog', default=True,
755 )
752 )
756 coreconfigitem(
753 coreconfigitem(
757 b'format',
754 b'format',
758 b'revlog-compression',
755 b'revlog-compression',
759 default=lambda: [b'zlib'],
756 default=lambda: [b'zlib'],
760 alias=[(b'experimental', b'format.compression')],
757 alias=[(b'experimental', b'format.compression')],
761 )
758 )
762 coreconfigitem(
759 coreconfigitem(
763 b'format', b'usefncache', default=True,
760 b'format', b'usefncache', default=True,
764 )
761 )
765 coreconfigitem(
762 coreconfigitem(
766 b'format', b'usegeneraldelta', default=True,
763 b'format', b'usegeneraldelta', default=True,
767 )
764 )
768 coreconfigitem(
765 coreconfigitem(
769 b'format', b'usestore', default=True,
766 b'format', b'usestore', default=True,
770 )
767 )
771 # Right now, the only efficient implement of the nodemap logic is in Rust, so
768 # Right now, the only efficient implement of the nodemap logic is in Rust, so
772 # the persistent nodemap feature needs to stay experimental as long as the Rust
769 # the persistent nodemap feature needs to stay experimental as long as the Rust
773 # extensions are an experimental feature.
770 # extensions are an experimental feature.
774 coreconfigitem(
771 coreconfigitem(
775 b'format', b'use-persistent-nodemap', default=False, experimental=True
772 b'format', b'use-persistent-nodemap', default=False, experimental=True
776 )
773 )
777 coreconfigitem(
774 coreconfigitem(
778 b'format',
775 b'format',
779 b'exp-use-copies-side-data-changeset',
776 b'exp-use-copies-side-data-changeset',
780 default=False,
777 default=False,
781 experimental=True,
778 experimental=True,
782 )
779 )
783 coreconfigitem(
780 coreconfigitem(
784 b'format', b'exp-use-side-data', default=False, experimental=True,
781 b'format', b'exp-use-side-data', default=False, experimental=True,
785 )
782 )
786 coreconfigitem(
783 coreconfigitem(
787 b'format', b'exp-share-safe', default=False, experimental=True,
784 b'format', b'exp-share-safe', default=False, experimental=True,
788 )
785 )
789 coreconfigitem(
786 coreconfigitem(
790 b'format', b'internal-phase', default=False, experimental=True,
787 b'format', b'internal-phase', default=False, experimental=True,
791 )
788 )
792 coreconfigitem(
789 coreconfigitem(
793 b'fsmonitor', b'warn_when_unused', default=True,
790 b'fsmonitor', b'warn_when_unused', default=True,
794 )
791 )
795 coreconfigitem(
792 coreconfigitem(
796 b'fsmonitor', b'warn_update_file_count', default=50000,
793 b'fsmonitor', b'warn_update_file_count', default=50000,
797 )
794 )
798 coreconfigitem(
795 coreconfigitem(
799 b'fsmonitor', b'warn_update_file_count_rust', default=400000,
796 b'fsmonitor', b'warn_update_file_count_rust', default=400000,
800 )
797 )
801 coreconfigitem(
798 coreconfigitem(
802 b'help', br'hidden-command\..*', default=False, generic=True,
799 b'help', br'hidden-command\..*', default=False, generic=True,
803 )
800 )
804 coreconfigitem(
801 coreconfigitem(
805 b'help', br'hidden-topic\..*', default=False, generic=True,
802 b'help', br'hidden-topic\..*', default=False, generic=True,
806 )
803 )
807 coreconfigitem(
804 coreconfigitem(
808 b'hooks', b'.*', default=dynamicdefault, generic=True,
805 b'hooks', b'.*', default=dynamicdefault, generic=True,
809 )
806 )
810 coreconfigitem(
807 coreconfigitem(
811 b'hgweb-paths', b'.*', default=list, generic=True,
808 b'hgweb-paths', b'.*', default=list, generic=True,
812 )
809 )
813 coreconfigitem(
810 coreconfigitem(
814 b'hostfingerprints', b'.*', default=list, generic=True,
811 b'hostfingerprints', b'.*', default=list, generic=True,
815 )
812 )
816 coreconfigitem(
813 coreconfigitem(
817 b'hostsecurity', b'ciphers', default=None,
814 b'hostsecurity', b'ciphers', default=None,
818 )
815 )
819 coreconfigitem(
816 coreconfigitem(
820 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
817 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
821 )
818 )
822 coreconfigitem(
819 coreconfigitem(
823 b'hostsecurity',
820 b'hostsecurity',
824 b'.*:minimumprotocol$',
821 b'.*:minimumprotocol$',
825 default=dynamicdefault,
822 default=dynamicdefault,
826 generic=True,
823 generic=True,
827 )
824 )
828 coreconfigitem(
825 coreconfigitem(
829 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
826 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
830 )
827 )
831 coreconfigitem(
828 coreconfigitem(
832 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
829 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
833 )
830 )
834 coreconfigitem(
831 coreconfigitem(
835 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
832 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
836 )
833 )
837
834
838 coreconfigitem(
835 coreconfigitem(
839 b'http_proxy', b'always', default=False,
836 b'http_proxy', b'always', default=False,
840 )
837 )
841 coreconfigitem(
838 coreconfigitem(
842 b'http_proxy', b'host', default=None,
839 b'http_proxy', b'host', default=None,
843 )
840 )
844 coreconfigitem(
841 coreconfigitem(
845 b'http_proxy', b'no', default=list,
842 b'http_proxy', b'no', default=list,
846 )
843 )
847 coreconfigitem(
844 coreconfigitem(
848 b'http_proxy', b'passwd', default=None,
845 b'http_proxy', b'passwd', default=None,
849 )
846 )
850 coreconfigitem(
847 coreconfigitem(
851 b'http_proxy', b'user', default=None,
848 b'http_proxy', b'user', default=None,
852 )
849 )
853
850
854 coreconfigitem(
851 coreconfigitem(
855 b'http', b'timeout', default=None,
852 b'http', b'timeout', default=None,
856 )
853 )
857
854
858 coreconfigitem(
855 coreconfigitem(
859 b'logtoprocess', b'commandexception', default=None,
856 b'logtoprocess', b'commandexception', default=None,
860 )
857 )
861 coreconfigitem(
858 coreconfigitem(
862 b'logtoprocess', b'commandfinish', default=None,
859 b'logtoprocess', b'commandfinish', default=None,
863 )
860 )
864 coreconfigitem(
861 coreconfigitem(
865 b'logtoprocess', b'command', default=None,
862 b'logtoprocess', b'command', default=None,
866 )
863 )
867 coreconfigitem(
864 coreconfigitem(
868 b'logtoprocess', b'develwarn', default=None,
865 b'logtoprocess', b'develwarn', default=None,
869 )
866 )
870 coreconfigitem(
867 coreconfigitem(
871 b'logtoprocess', b'uiblocked', default=None,
868 b'logtoprocess', b'uiblocked', default=None,
872 )
869 )
873 coreconfigitem(
870 coreconfigitem(
874 b'merge', b'checkunknown', default=b'abort',
871 b'merge', b'checkunknown', default=b'abort',
875 )
872 )
876 coreconfigitem(
873 coreconfigitem(
877 b'merge', b'checkignored', default=b'abort',
874 b'merge', b'checkignored', default=b'abort',
878 )
875 )
879 coreconfigitem(
876 coreconfigitem(
880 b'experimental', b'merge.checkpathconflicts', default=False,
877 b'experimental', b'merge.checkpathconflicts', default=False,
881 )
878 )
882 coreconfigitem(
879 coreconfigitem(
883 b'merge', b'followcopies', default=True,
880 b'merge', b'followcopies', default=True,
884 )
881 )
885 coreconfigitem(
882 coreconfigitem(
886 b'merge', b'on-failure', default=b'continue',
883 b'merge', b'on-failure', default=b'continue',
887 )
884 )
888 coreconfigitem(
885 coreconfigitem(
889 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
886 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
890 )
887 )
891 coreconfigitem(
888 coreconfigitem(
892 b'merge', b'strict-capability-check', default=False,
889 b'merge', b'strict-capability-check', default=False,
893 )
890 )
894 coreconfigitem(
891 coreconfigitem(
895 b'merge-tools', b'.*', default=None, generic=True,
892 b'merge-tools', b'.*', default=None, generic=True,
896 )
893 )
897 coreconfigitem(
894 coreconfigitem(
898 b'merge-tools',
895 b'merge-tools',
899 br'.*\.args$',
896 br'.*\.args$',
900 default=b"$local $base $other",
897 default=b"$local $base $other",
901 generic=True,
898 generic=True,
902 priority=-1,
899 priority=-1,
903 )
900 )
904 coreconfigitem(
901 coreconfigitem(
905 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
902 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
906 )
903 )
907 coreconfigitem(
904 coreconfigitem(
908 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
905 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
909 )
906 )
910 coreconfigitem(
907 coreconfigitem(
911 b'merge-tools',
908 b'merge-tools',
912 br'.*\.checkchanged$',
909 br'.*\.checkchanged$',
913 default=False,
910 default=False,
914 generic=True,
911 generic=True,
915 priority=-1,
912 priority=-1,
916 )
913 )
917 coreconfigitem(
914 coreconfigitem(
918 b'merge-tools',
915 b'merge-tools',
919 br'.*\.executable$',
916 br'.*\.executable$',
920 default=dynamicdefault,
917 default=dynamicdefault,
921 generic=True,
918 generic=True,
922 priority=-1,
919 priority=-1,
923 )
920 )
924 coreconfigitem(
921 coreconfigitem(
925 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
922 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
926 )
923 )
927 coreconfigitem(
924 coreconfigitem(
928 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
925 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
929 )
926 )
930 coreconfigitem(
927 coreconfigitem(
931 b'merge-tools',
928 b'merge-tools',
932 br'.*\.mergemarkers$',
929 br'.*\.mergemarkers$',
933 default=b'basic',
930 default=b'basic',
934 generic=True,
931 generic=True,
935 priority=-1,
932 priority=-1,
936 )
933 )
937 coreconfigitem(
934 coreconfigitem(
938 b'merge-tools',
935 b'merge-tools',
939 br'.*\.mergemarkertemplate$',
936 br'.*\.mergemarkertemplate$',
940 default=dynamicdefault, # take from ui.mergemarkertemplate
937 default=dynamicdefault, # take from ui.mergemarkertemplate
941 generic=True,
938 generic=True,
942 priority=-1,
939 priority=-1,
943 )
940 )
944 coreconfigitem(
941 coreconfigitem(
945 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
942 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
946 )
943 )
947 coreconfigitem(
944 coreconfigitem(
948 b'merge-tools',
945 b'merge-tools',
949 br'.*\.premerge$',
946 br'.*\.premerge$',
950 default=dynamicdefault,
947 default=dynamicdefault,
951 generic=True,
948 generic=True,
952 priority=-1,
949 priority=-1,
953 )
950 )
954 coreconfigitem(
951 coreconfigitem(
955 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
952 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
956 )
953 )
957 coreconfigitem(
954 coreconfigitem(
958 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
955 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
959 )
956 )
960 coreconfigitem(
957 coreconfigitem(
961 b'pager', b'ignore', default=list,
958 b'pager', b'ignore', default=list,
962 )
959 )
963 coreconfigitem(
960 coreconfigitem(
964 b'pager', b'pager', default=dynamicdefault,
961 b'pager', b'pager', default=dynamicdefault,
965 )
962 )
966 coreconfigitem(
963 coreconfigitem(
967 b'patch', b'eol', default=b'strict',
964 b'patch', b'eol', default=b'strict',
968 )
965 )
969 coreconfigitem(
966 coreconfigitem(
970 b'patch', b'fuzz', default=2,
967 b'patch', b'fuzz', default=2,
971 )
968 )
972 coreconfigitem(
969 coreconfigitem(
973 b'paths', b'default', default=None,
970 b'paths', b'default', default=None,
974 )
971 )
975 coreconfigitem(
972 coreconfigitem(
976 b'paths', b'default-push', default=None,
973 b'paths', b'default-push', default=None,
977 )
974 )
978 coreconfigitem(
975 coreconfigitem(
979 b'paths', b'.*', default=None, generic=True,
976 b'paths', b'.*', default=None, generic=True,
980 )
977 )
981 coreconfigitem(
978 coreconfigitem(
982 b'phases', b'checksubrepos', default=b'follow',
979 b'phases', b'checksubrepos', default=b'follow',
983 )
980 )
984 coreconfigitem(
981 coreconfigitem(
985 b'phases', b'new-commit', default=b'draft',
982 b'phases', b'new-commit', default=b'draft',
986 )
983 )
987 coreconfigitem(
984 coreconfigitem(
988 b'phases', b'publish', default=True,
985 b'phases', b'publish', default=True,
989 )
986 )
990 coreconfigitem(
987 coreconfigitem(
991 b'profiling', b'enabled', default=False,
988 b'profiling', b'enabled', default=False,
992 )
989 )
993 coreconfigitem(
990 coreconfigitem(
994 b'profiling', b'format', default=b'text',
991 b'profiling', b'format', default=b'text',
995 )
992 )
996 coreconfigitem(
993 coreconfigitem(
997 b'profiling', b'freq', default=1000,
994 b'profiling', b'freq', default=1000,
998 )
995 )
999 coreconfigitem(
996 coreconfigitem(
1000 b'profiling', b'limit', default=30,
997 b'profiling', b'limit', default=30,
1001 )
998 )
1002 coreconfigitem(
999 coreconfigitem(
1003 b'profiling', b'nested', default=0,
1000 b'profiling', b'nested', default=0,
1004 )
1001 )
1005 coreconfigitem(
1002 coreconfigitem(
1006 b'profiling', b'output', default=None,
1003 b'profiling', b'output', default=None,
1007 )
1004 )
1008 coreconfigitem(
1005 coreconfigitem(
1009 b'profiling', b'showmax', default=0.999,
1006 b'profiling', b'showmax', default=0.999,
1010 )
1007 )
1011 coreconfigitem(
1008 coreconfigitem(
1012 b'profiling', b'showmin', default=dynamicdefault,
1009 b'profiling', b'showmin', default=dynamicdefault,
1013 )
1010 )
1014 coreconfigitem(
1011 coreconfigitem(
1015 b'profiling', b'showtime', default=True,
1012 b'profiling', b'showtime', default=True,
1016 )
1013 )
1017 coreconfigitem(
1014 coreconfigitem(
1018 b'profiling', b'sort', default=b'inlinetime',
1015 b'profiling', b'sort', default=b'inlinetime',
1019 )
1016 )
1020 coreconfigitem(
1017 coreconfigitem(
1021 b'profiling', b'statformat', default=b'hotpath',
1018 b'profiling', b'statformat', default=b'hotpath',
1022 )
1019 )
1023 coreconfigitem(
1020 coreconfigitem(
1024 b'profiling', b'time-track', default=dynamicdefault,
1021 b'profiling', b'time-track', default=dynamicdefault,
1025 )
1022 )
1026 coreconfigitem(
1023 coreconfigitem(
1027 b'profiling', b'type', default=b'stat',
1024 b'profiling', b'type', default=b'stat',
1028 )
1025 )
1029 coreconfigitem(
1026 coreconfigitem(
1030 b'progress', b'assume-tty', default=False,
1027 b'progress', b'assume-tty', default=False,
1031 )
1028 )
1032 coreconfigitem(
1029 coreconfigitem(
1033 b'progress', b'changedelay', default=1,
1030 b'progress', b'changedelay', default=1,
1034 )
1031 )
1035 coreconfigitem(
1032 coreconfigitem(
1036 b'progress', b'clear-complete', default=True,
1033 b'progress', b'clear-complete', default=True,
1037 )
1034 )
1038 coreconfigitem(
1035 coreconfigitem(
1039 b'progress', b'debug', default=False,
1036 b'progress', b'debug', default=False,
1040 )
1037 )
1041 coreconfigitem(
1038 coreconfigitem(
1042 b'progress', b'delay', default=3,
1039 b'progress', b'delay', default=3,
1043 )
1040 )
1044 coreconfigitem(
1041 coreconfigitem(
1045 b'progress', b'disable', default=False,
1042 b'progress', b'disable', default=False,
1046 )
1043 )
1047 coreconfigitem(
1044 coreconfigitem(
1048 b'progress', b'estimateinterval', default=60.0,
1045 b'progress', b'estimateinterval', default=60.0,
1049 )
1046 )
1050 coreconfigitem(
1047 coreconfigitem(
1051 b'progress',
1048 b'progress',
1052 b'format',
1049 b'format',
1053 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1050 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1054 )
1051 )
1055 coreconfigitem(
1052 coreconfigitem(
1056 b'progress', b'refresh', default=0.1,
1053 b'progress', b'refresh', default=0.1,
1057 )
1054 )
1058 coreconfigitem(
1055 coreconfigitem(
1059 b'progress', b'width', default=dynamicdefault,
1056 b'progress', b'width', default=dynamicdefault,
1060 )
1057 )
1061 coreconfigitem(
1058 coreconfigitem(
1062 b'pull', b'confirm', default=False,
1059 b'pull', b'confirm', default=False,
1063 )
1060 )
1064 coreconfigitem(
1061 coreconfigitem(
1065 b'push', b'pushvars.server', default=False,
1062 b'push', b'pushvars.server', default=False,
1066 )
1063 )
1067 coreconfigitem(
1064 coreconfigitem(
1068 b'rewrite',
1065 b'rewrite',
1069 b'backup-bundle',
1066 b'backup-bundle',
1070 default=True,
1067 default=True,
1071 alias=[(b'ui', b'history-editing-backup')],
1068 alias=[(b'ui', b'history-editing-backup')],
1072 )
1069 )
1073 coreconfigitem(
1070 coreconfigitem(
1074 b'rewrite', b'update-timestamp', default=False,
1071 b'rewrite', b'update-timestamp', default=False,
1075 )
1072 )
1076 coreconfigitem(
1073 coreconfigitem(
1077 b'rewrite', b'empty-successor', default=b'skip', experimental=True,
1074 b'rewrite', b'empty-successor', default=b'skip', experimental=True,
1078 )
1075 )
1079 coreconfigitem(
1076 coreconfigitem(
1080 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1077 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1081 )
1078 )
1082 coreconfigitem(
1079 coreconfigitem(
1083 b'storage',
1080 b'storage',
1084 b'revlog.optimize-delta-parent-choice',
1081 b'revlog.optimize-delta-parent-choice',
1085 default=True,
1082 default=True,
1086 alias=[(b'format', b'aggressivemergedeltas')],
1083 alias=[(b'format', b'aggressivemergedeltas')],
1087 )
1084 )
1088 # experimental as long as rust is experimental (or a C version is implemented)
1085 # experimental as long as rust is experimental (or a C version is implemented)
1089 coreconfigitem(
1086 coreconfigitem(
1090 b'storage', b'revlog.nodemap.mmap', default=True, experimental=True
1087 b'storage', b'revlog.nodemap.mmap', default=True, experimental=True
1091 )
1088 )
1092 # experimental as long as format.use-persistent-nodemap is.
1089 # experimental as long as format.use-persistent-nodemap is.
1093 coreconfigitem(
1090 coreconfigitem(
1094 b'storage', b'revlog.nodemap.mode', default=b'compat', experimental=True
1091 b'storage', b'revlog.nodemap.mode', default=b'compat', experimental=True
1095 )
1092 )
1096 coreconfigitem(
1093 coreconfigitem(
1097 b'storage', b'revlog.reuse-external-delta', default=True,
1094 b'storage', b'revlog.reuse-external-delta', default=True,
1098 )
1095 )
1099 coreconfigitem(
1096 coreconfigitem(
1100 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1097 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1101 )
1098 )
1102 coreconfigitem(
1099 coreconfigitem(
1103 b'storage', b'revlog.zlib.level', default=None,
1100 b'storage', b'revlog.zlib.level', default=None,
1104 )
1101 )
1105 coreconfigitem(
1102 coreconfigitem(
1106 b'storage', b'revlog.zstd.level', default=None,
1103 b'storage', b'revlog.zstd.level', default=None,
1107 )
1104 )
1108 coreconfigitem(
1105 coreconfigitem(
1109 b'server', b'bookmarks-pushkey-compat', default=True,
1106 b'server', b'bookmarks-pushkey-compat', default=True,
1110 )
1107 )
1111 coreconfigitem(
1108 coreconfigitem(
1112 b'server', b'bundle1', default=True,
1109 b'server', b'bundle1', default=True,
1113 )
1110 )
1114 coreconfigitem(
1111 coreconfigitem(
1115 b'server', b'bundle1gd', default=None,
1112 b'server', b'bundle1gd', default=None,
1116 )
1113 )
1117 coreconfigitem(
1114 coreconfigitem(
1118 b'server', b'bundle1.pull', default=None,
1115 b'server', b'bundle1.pull', default=None,
1119 )
1116 )
1120 coreconfigitem(
1117 coreconfigitem(
1121 b'server', b'bundle1gd.pull', default=None,
1118 b'server', b'bundle1gd.pull', default=None,
1122 )
1119 )
1123 coreconfigitem(
1120 coreconfigitem(
1124 b'server', b'bundle1.push', default=None,
1121 b'server', b'bundle1.push', default=None,
1125 )
1122 )
1126 coreconfigitem(
1123 coreconfigitem(
1127 b'server', b'bundle1gd.push', default=None,
1124 b'server', b'bundle1gd.push', default=None,
1128 )
1125 )
1129 coreconfigitem(
1126 coreconfigitem(
1130 b'server',
1127 b'server',
1131 b'bundle2.stream',
1128 b'bundle2.stream',
1132 default=True,
1129 default=True,
1133 alias=[(b'experimental', b'bundle2.stream')],
1130 alias=[(b'experimental', b'bundle2.stream')],
1134 )
1131 )
1135 coreconfigitem(
1132 coreconfigitem(
1136 b'server', b'compressionengines', default=list,
1133 b'server', b'compressionengines', default=list,
1137 )
1134 )
1138 coreconfigitem(
1135 coreconfigitem(
1139 b'server', b'concurrent-push-mode', default=b'check-related',
1136 b'server', b'concurrent-push-mode', default=b'check-related',
1140 )
1137 )
1141 coreconfigitem(
1138 coreconfigitem(
1142 b'server', b'disablefullbundle', default=False,
1139 b'server', b'disablefullbundle', default=False,
1143 )
1140 )
1144 coreconfigitem(
1141 coreconfigitem(
1145 b'server', b'maxhttpheaderlen', default=1024,
1142 b'server', b'maxhttpheaderlen', default=1024,
1146 )
1143 )
1147 coreconfigitem(
1144 coreconfigitem(
1148 b'server', b'pullbundle', default=False,
1145 b'server', b'pullbundle', default=False,
1149 )
1146 )
1150 coreconfigitem(
1147 coreconfigitem(
1151 b'server', b'preferuncompressed', default=False,
1148 b'server', b'preferuncompressed', default=False,
1152 )
1149 )
1153 coreconfigitem(
1150 coreconfigitem(
1154 b'server', b'streamunbundle', default=False,
1151 b'server', b'streamunbundle', default=False,
1155 )
1152 )
1156 coreconfigitem(
1153 coreconfigitem(
1157 b'server', b'uncompressed', default=True,
1154 b'server', b'uncompressed', default=True,
1158 )
1155 )
1159 coreconfigitem(
1156 coreconfigitem(
1160 b'server', b'uncompressedallowsecret', default=False,
1157 b'server', b'uncompressedallowsecret', default=False,
1161 )
1158 )
1162 coreconfigitem(
1159 coreconfigitem(
1163 b'server', b'view', default=b'served',
1160 b'server', b'view', default=b'served',
1164 )
1161 )
1165 coreconfigitem(
1162 coreconfigitem(
1166 b'server', b'validate', default=False,
1163 b'server', b'validate', default=False,
1167 )
1164 )
1168 coreconfigitem(
1165 coreconfigitem(
1169 b'server', b'zliblevel', default=-1,
1166 b'server', b'zliblevel', default=-1,
1170 )
1167 )
1171 coreconfigitem(
1168 coreconfigitem(
1172 b'server', b'zstdlevel', default=3,
1169 b'server', b'zstdlevel', default=3,
1173 )
1170 )
1174 coreconfigitem(
1171 coreconfigitem(
1175 b'share', b'pool', default=None,
1172 b'share', b'pool', default=None,
1176 )
1173 )
1177 coreconfigitem(
1174 coreconfigitem(
1178 b'share', b'poolnaming', default=b'identity',
1175 b'share', b'poolnaming', default=b'identity',
1179 )
1176 )
1180 coreconfigitem(
1177 coreconfigitem(
1181 b'shelve', b'maxbackups', default=10,
1178 b'shelve', b'maxbackups', default=10,
1182 )
1179 )
1183 coreconfigitem(
1180 coreconfigitem(
1184 b'smtp', b'host', default=None,
1181 b'smtp', b'host', default=None,
1185 )
1182 )
1186 coreconfigitem(
1183 coreconfigitem(
1187 b'smtp', b'local_hostname', default=None,
1184 b'smtp', b'local_hostname', default=None,
1188 )
1185 )
1189 coreconfigitem(
1186 coreconfigitem(
1190 b'smtp', b'password', default=None,
1187 b'smtp', b'password', default=None,
1191 )
1188 )
1192 coreconfigitem(
1189 coreconfigitem(
1193 b'smtp', b'port', default=dynamicdefault,
1190 b'smtp', b'port', default=dynamicdefault,
1194 )
1191 )
1195 coreconfigitem(
1192 coreconfigitem(
1196 b'smtp', b'tls', default=b'none',
1193 b'smtp', b'tls', default=b'none',
1197 )
1194 )
1198 coreconfigitem(
1195 coreconfigitem(
1199 b'smtp', b'username', default=None,
1196 b'smtp', b'username', default=None,
1200 )
1197 )
1201 coreconfigitem(
1198 coreconfigitem(
1202 b'sparse', b'missingwarning', default=True, experimental=True,
1199 b'sparse', b'missingwarning', default=True, experimental=True,
1203 )
1200 )
1204 coreconfigitem(
1201 coreconfigitem(
1205 b'subrepos',
1202 b'subrepos',
1206 b'allowed',
1203 b'allowed',
1207 default=dynamicdefault, # to make backporting simpler
1204 default=dynamicdefault, # to make backporting simpler
1208 )
1205 )
1209 coreconfigitem(
1206 coreconfigitem(
1210 b'subrepos', b'hg:allowed', default=dynamicdefault,
1207 b'subrepos', b'hg:allowed', default=dynamicdefault,
1211 )
1208 )
1212 coreconfigitem(
1209 coreconfigitem(
1213 b'subrepos', b'git:allowed', default=dynamicdefault,
1210 b'subrepos', b'git:allowed', default=dynamicdefault,
1214 )
1211 )
1215 coreconfigitem(
1212 coreconfigitem(
1216 b'subrepos', b'svn:allowed', default=dynamicdefault,
1213 b'subrepos', b'svn:allowed', default=dynamicdefault,
1217 )
1214 )
1218 coreconfigitem(
1215 coreconfigitem(
1219 b'templates', b'.*', default=None, generic=True,
1216 b'templates', b'.*', default=None, generic=True,
1220 )
1217 )
1221 coreconfigitem(
1218 coreconfigitem(
1222 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1219 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1223 )
1220 )
1224 coreconfigitem(
1221 coreconfigitem(
1225 b'trusted', b'groups', default=list,
1222 b'trusted', b'groups', default=list,
1226 )
1223 )
1227 coreconfigitem(
1224 coreconfigitem(
1228 b'trusted', b'users', default=list,
1225 b'trusted', b'users', default=list,
1229 )
1226 )
1230 coreconfigitem(
1227 coreconfigitem(
1231 b'ui', b'_usedassubrepo', default=False,
1228 b'ui', b'_usedassubrepo', default=False,
1232 )
1229 )
1233 coreconfigitem(
1230 coreconfigitem(
1234 b'ui', b'allowemptycommit', default=False,
1231 b'ui', b'allowemptycommit', default=False,
1235 )
1232 )
1236 coreconfigitem(
1233 coreconfigitem(
1237 b'ui', b'archivemeta', default=True,
1234 b'ui', b'archivemeta', default=True,
1238 )
1235 )
1239 coreconfigitem(
1236 coreconfigitem(
1240 b'ui', b'askusername', default=False,
1237 b'ui', b'askusername', default=False,
1241 )
1238 )
1242 coreconfigitem(
1239 coreconfigitem(
1243 b'ui', b'available-memory', default=None,
1240 b'ui', b'available-memory', default=None,
1244 )
1241 )
1245
1242
1246 coreconfigitem(
1243 coreconfigitem(
1247 b'ui', b'clonebundlefallback', default=False,
1244 b'ui', b'clonebundlefallback', default=False,
1248 )
1245 )
1249 coreconfigitem(
1246 coreconfigitem(
1250 b'ui', b'clonebundleprefers', default=list,
1247 b'ui', b'clonebundleprefers', default=list,
1251 )
1248 )
1252 coreconfigitem(
1249 coreconfigitem(
1253 b'ui', b'clonebundles', default=True,
1250 b'ui', b'clonebundles', default=True,
1254 )
1251 )
1255 coreconfigitem(
1252 coreconfigitem(
1256 b'ui', b'color', default=b'auto',
1253 b'ui', b'color', default=b'auto',
1257 )
1254 )
1258 coreconfigitem(
1255 coreconfigitem(
1259 b'ui', b'commitsubrepos', default=False,
1256 b'ui', b'commitsubrepos', default=False,
1260 )
1257 )
1261 coreconfigitem(
1258 coreconfigitem(
1262 b'ui', b'debug', default=False,
1259 b'ui', b'debug', default=False,
1263 )
1260 )
1264 coreconfigitem(
1261 coreconfigitem(
1265 b'ui', b'debugger', default=None,
1262 b'ui', b'debugger', default=None,
1266 )
1263 )
1267 coreconfigitem(
1264 coreconfigitem(
1268 b'ui', b'editor', default=dynamicdefault,
1265 b'ui', b'editor', default=dynamicdefault,
1269 )
1266 )
1270 coreconfigitem(
1267 coreconfigitem(
1271 b'ui', b'fallbackencoding', default=None,
1268 b'ui', b'fallbackencoding', default=None,
1272 )
1269 )
1273 coreconfigitem(
1270 coreconfigitem(
1274 b'ui', b'forcecwd', default=None,
1271 b'ui', b'forcecwd', default=None,
1275 )
1272 )
1276 coreconfigitem(
1273 coreconfigitem(
1277 b'ui', b'forcemerge', default=None,
1274 b'ui', b'forcemerge', default=None,
1278 )
1275 )
1279 coreconfigitem(
1276 coreconfigitem(
1280 b'ui', b'formatdebug', default=False,
1277 b'ui', b'formatdebug', default=False,
1281 )
1278 )
1282 coreconfigitem(
1279 coreconfigitem(
1283 b'ui', b'formatjson', default=False,
1280 b'ui', b'formatjson', default=False,
1284 )
1281 )
1285 coreconfigitem(
1282 coreconfigitem(
1286 b'ui', b'formatted', default=None,
1283 b'ui', b'formatted', default=None,
1287 )
1284 )
1288 coreconfigitem(
1285 coreconfigitem(
1289 b'ui', b'graphnodetemplate', default=None,
1286 b'ui', b'graphnodetemplate', default=None,
1290 )
1287 )
1291 coreconfigitem(
1288 coreconfigitem(
1292 b'ui', b'interactive', default=None,
1289 b'ui', b'interactive', default=None,
1293 )
1290 )
1294 coreconfigitem(
1291 coreconfigitem(
1295 b'ui', b'interface', default=None,
1292 b'ui', b'interface', default=None,
1296 )
1293 )
1297 coreconfigitem(
1294 coreconfigitem(
1298 b'ui', b'interface.chunkselector', default=None,
1295 b'ui', b'interface.chunkselector', default=None,
1299 )
1296 )
1300 coreconfigitem(
1297 coreconfigitem(
1301 b'ui', b'large-file-limit', default=10000000,
1298 b'ui', b'large-file-limit', default=10000000,
1302 )
1299 )
1303 coreconfigitem(
1300 coreconfigitem(
1304 b'ui', b'logblockedtimes', default=False,
1301 b'ui', b'logblockedtimes', default=False,
1305 )
1302 )
1306 coreconfigitem(
1303 coreconfigitem(
1307 b'ui', b'logtemplate', default=None,
1304 b'ui', b'logtemplate', default=None,
1308 )
1305 )
1309 coreconfigitem(
1306 coreconfigitem(
1310 b'ui', b'merge', default=None,
1307 b'ui', b'merge', default=None,
1311 )
1308 )
1312 coreconfigitem(
1309 coreconfigitem(
1313 b'ui', b'mergemarkers', default=b'basic',
1310 b'ui', b'mergemarkers', default=b'basic',
1314 )
1311 )
1315 coreconfigitem(
1312 coreconfigitem(
1316 b'ui',
1313 b'ui',
1317 b'mergemarkertemplate',
1314 b'mergemarkertemplate',
1318 default=(
1315 default=(
1319 b'{node|short} '
1316 b'{node|short} '
1320 b'{ifeq(tags, "tip", "", '
1317 b'{ifeq(tags, "tip", "", '
1321 b'ifeq(tags, "", "", "{tags} "))}'
1318 b'ifeq(tags, "", "", "{tags} "))}'
1322 b'{if(bookmarks, "{bookmarks} ")}'
1319 b'{if(bookmarks, "{bookmarks} ")}'
1323 b'{ifeq(branch, "default", "", "{branch} ")}'
1320 b'{ifeq(branch, "default", "", "{branch} ")}'
1324 b'- {author|user}: {desc|firstline}'
1321 b'- {author|user}: {desc|firstline}'
1325 ),
1322 ),
1326 )
1323 )
1327 coreconfigitem(
1324 coreconfigitem(
1328 b'ui', b'message-output', default=b'stdio',
1325 b'ui', b'message-output', default=b'stdio',
1329 )
1326 )
1330 coreconfigitem(
1327 coreconfigitem(
1331 b'ui', b'nontty', default=False,
1328 b'ui', b'nontty', default=False,
1332 )
1329 )
1333 coreconfigitem(
1330 coreconfigitem(
1334 b'ui', b'origbackuppath', default=None,
1331 b'ui', b'origbackuppath', default=None,
1335 )
1332 )
1336 coreconfigitem(
1333 coreconfigitem(
1337 b'ui', b'paginate', default=True,
1334 b'ui', b'paginate', default=True,
1338 )
1335 )
1339 coreconfigitem(
1336 coreconfigitem(
1340 b'ui', b'patch', default=None,
1337 b'ui', b'patch', default=None,
1341 )
1338 )
1342 coreconfigitem(
1339 coreconfigitem(
1343 b'ui', b'pre-merge-tool-output-template', default=None,
1340 b'ui', b'pre-merge-tool-output-template', default=None,
1344 )
1341 )
1345 coreconfigitem(
1342 coreconfigitem(
1346 b'ui', b'portablefilenames', default=b'warn',
1343 b'ui', b'portablefilenames', default=b'warn',
1347 )
1344 )
1348 coreconfigitem(
1345 coreconfigitem(
1349 b'ui', b'promptecho', default=False,
1346 b'ui', b'promptecho', default=False,
1350 )
1347 )
1351 coreconfigitem(
1348 coreconfigitem(
1352 b'ui', b'quiet', default=False,
1349 b'ui', b'quiet', default=False,
1353 )
1350 )
1354 coreconfigitem(
1351 coreconfigitem(
1355 b'ui', b'quietbookmarkmove', default=False,
1352 b'ui', b'quietbookmarkmove', default=False,
1356 )
1353 )
1357 coreconfigitem(
1354 coreconfigitem(
1358 b'ui', b'relative-paths', default=b'legacy',
1355 b'ui', b'relative-paths', default=b'legacy',
1359 )
1356 )
1360 coreconfigitem(
1357 coreconfigitem(
1361 b'ui', b'remotecmd', default=b'hg',
1358 b'ui', b'remotecmd', default=b'hg',
1362 )
1359 )
1363 coreconfigitem(
1360 coreconfigitem(
1364 b'ui', b'report_untrusted', default=True,
1361 b'ui', b'report_untrusted', default=True,
1365 )
1362 )
1366 coreconfigitem(
1363 coreconfigitem(
1367 b'ui', b'rollback', default=True,
1364 b'ui', b'rollback', default=True,
1368 )
1365 )
1369 coreconfigitem(
1366 coreconfigitem(
1370 b'ui', b'signal-safe-lock', default=True,
1367 b'ui', b'signal-safe-lock', default=True,
1371 )
1368 )
1372 coreconfigitem(
1369 coreconfigitem(
1373 b'ui', b'slash', default=False,
1370 b'ui', b'slash', default=False,
1374 )
1371 )
1375 coreconfigitem(
1372 coreconfigitem(
1376 b'ui', b'ssh', default=b'ssh',
1373 b'ui', b'ssh', default=b'ssh',
1377 )
1374 )
1378 coreconfigitem(
1375 coreconfigitem(
1379 b'ui', b'ssherrorhint', default=None,
1376 b'ui', b'ssherrorhint', default=None,
1380 )
1377 )
1381 coreconfigitem(
1378 coreconfigitem(
1382 b'ui', b'statuscopies', default=False,
1379 b'ui', b'statuscopies', default=False,
1383 )
1380 )
1384 coreconfigitem(
1381 coreconfigitem(
1385 b'ui', b'strict', default=False,
1382 b'ui', b'strict', default=False,
1386 )
1383 )
1387 coreconfigitem(
1384 coreconfigitem(
1388 b'ui', b'style', default=b'',
1385 b'ui', b'style', default=b'',
1389 )
1386 )
1390 coreconfigitem(
1387 coreconfigitem(
1391 b'ui', b'supportcontact', default=None,
1388 b'ui', b'supportcontact', default=None,
1392 )
1389 )
1393 coreconfigitem(
1390 coreconfigitem(
1394 b'ui', b'textwidth', default=78,
1391 b'ui', b'textwidth', default=78,
1395 )
1392 )
1396 coreconfigitem(
1393 coreconfigitem(
1397 b'ui', b'timeout', default=b'600',
1394 b'ui', b'timeout', default=b'600',
1398 )
1395 )
1399 coreconfigitem(
1396 coreconfigitem(
1400 b'ui', b'timeout.warn', default=0,
1397 b'ui', b'timeout.warn', default=0,
1401 )
1398 )
1402 coreconfigitem(
1399 coreconfigitem(
1403 b'ui', b'timestamp-output', default=False,
1400 b'ui', b'timestamp-output', default=False,
1404 )
1401 )
1405 coreconfigitem(
1402 coreconfigitem(
1406 b'ui', b'traceback', default=False,
1403 b'ui', b'traceback', default=False,
1407 )
1404 )
1408 coreconfigitem(
1405 coreconfigitem(
1409 b'ui', b'tweakdefaults', default=False,
1406 b'ui', b'tweakdefaults', default=False,
1410 )
1407 )
1411 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1408 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1412 coreconfigitem(
1409 coreconfigitem(
1413 b'ui', b'verbose', default=False,
1410 b'ui', b'verbose', default=False,
1414 )
1411 )
1415 coreconfigitem(
1412 coreconfigitem(
1416 b'verify', b'skipflags', default=None,
1413 b'verify', b'skipflags', default=None,
1417 )
1414 )
1418 coreconfigitem(
1415 coreconfigitem(
1419 b'web', b'allowbz2', default=False,
1416 b'web', b'allowbz2', default=False,
1420 )
1417 )
1421 coreconfigitem(
1418 coreconfigitem(
1422 b'web', b'allowgz', default=False,
1419 b'web', b'allowgz', default=False,
1423 )
1420 )
1424 coreconfigitem(
1421 coreconfigitem(
1425 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1422 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1426 )
1423 )
1427 coreconfigitem(
1424 coreconfigitem(
1428 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1425 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1429 )
1426 )
1430 coreconfigitem(
1427 coreconfigitem(
1431 b'web', b'allowzip', default=False,
1428 b'web', b'allowzip', default=False,
1432 )
1429 )
1433 coreconfigitem(
1430 coreconfigitem(
1434 b'web', b'archivesubrepos', default=False,
1431 b'web', b'archivesubrepos', default=False,
1435 )
1432 )
1436 coreconfigitem(
1433 coreconfigitem(
1437 b'web', b'cache', default=True,
1434 b'web', b'cache', default=True,
1438 )
1435 )
1439 coreconfigitem(
1436 coreconfigitem(
1440 b'web', b'comparisoncontext', default=5,
1437 b'web', b'comparisoncontext', default=5,
1441 )
1438 )
1442 coreconfigitem(
1439 coreconfigitem(
1443 b'web', b'contact', default=None,
1440 b'web', b'contact', default=None,
1444 )
1441 )
1445 coreconfigitem(
1442 coreconfigitem(
1446 b'web', b'deny_push', default=list,
1443 b'web', b'deny_push', default=list,
1447 )
1444 )
1448 coreconfigitem(
1445 coreconfigitem(
1449 b'web', b'guessmime', default=False,
1446 b'web', b'guessmime', default=False,
1450 )
1447 )
1451 coreconfigitem(
1448 coreconfigitem(
1452 b'web', b'hidden', default=False,
1449 b'web', b'hidden', default=False,
1453 )
1450 )
1454 coreconfigitem(
1451 coreconfigitem(
1455 b'web', b'labels', default=list,
1452 b'web', b'labels', default=list,
1456 )
1453 )
1457 coreconfigitem(
1454 coreconfigitem(
1458 b'web', b'logoimg', default=b'hglogo.png',
1455 b'web', b'logoimg', default=b'hglogo.png',
1459 )
1456 )
1460 coreconfigitem(
1457 coreconfigitem(
1461 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1458 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1462 )
1459 )
1463 coreconfigitem(
1460 coreconfigitem(
1464 b'web', b'accesslog', default=b'-',
1461 b'web', b'accesslog', default=b'-',
1465 )
1462 )
1466 coreconfigitem(
1463 coreconfigitem(
1467 b'web', b'address', default=b'',
1464 b'web', b'address', default=b'',
1468 )
1465 )
1469 coreconfigitem(
1466 coreconfigitem(
1470 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1467 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1471 )
1468 )
1472 coreconfigitem(
1469 coreconfigitem(
1473 b'web', b'allow_read', default=list,
1470 b'web', b'allow_read', default=list,
1474 )
1471 )
1475 coreconfigitem(
1472 coreconfigitem(
1476 b'web', b'baseurl', default=None,
1473 b'web', b'baseurl', default=None,
1477 )
1474 )
1478 coreconfigitem(
1475 coreconfigitem(
1479 b'web', b'cacerts', default=None,
1476 b'web', b'cacerts', default=None,
1480 )
1477 )
1481 coreconfigitem(
1478 coreconfigitem(
1482 b'web', b'certificate', default=None,
1479 b'web', b'certificate', default=None,
1483 )
1480 )
1484 coreconfigitem(
1481 coreconfigitem(
1485 b'web', b'collapse', default=False,
1482 b'web', b'collapse', default=False,
1486 )
1483 )
1487 coreconfigitem(
1484 coreconfigitem(
1488 b'web', b'csp', default=None,
1485 b'web', b'csp', default=None,
1489 )
1486 )
1490 coreconfigitem(
1487 coreconfigitem(
1491 b'web', b'deny_read', default=list,
1488 b'web', b'deny_read', default=list,
1492 )
1489 )
1493 coreconfigitem(
1490 coreconfigitem(
1494 b'web', b'descend', default=True,
1491 b'web', b'descend', default=True,
1495 )
1492 )
1496 coreconfigitem(
1493 coreconfigitem(
1497 b'web', b'description', default=b"",
1494 b'web', b'description', default=b"",
1498 )
1495 )
1499 coreconfigitem(
1496 coreconfigitem(
1500 b'web', b'encoding', default=lambda: encoding.encoding,
1497 b'web', b'encoding', default=lambda: encoding.encoding,
1501 )
1498 )
1502 coreconfigitem(
1499 coreconfigitem(
1503 b'web', b'errorlog', default=b'-',
1500 b'web', b'errorlog', default=b'-',
1504 )
1501 )
1505 coreconfigitem(
1502 coreconfigitem(
1506 b'web', b'ipv6', default=False,
1503 b'web', b'ipv6', default=False,
1507 )
1504 )
1508 coreconfigitem(
1505 coreconfigitem(
1509 b'web', b'maxchanges', default=10,
1506 b'web', b'maxchanges', default=10,
1510 )
1507 )
1511 coreconfigitem(
1508 coreconfigitem(
1512 b'web', b'maxfiles', default=10,
1509 b'web', b'maxfiles', default=10,
1513 )
1510 )
1514 coreconfigitem(
1511 coreconfigitem(
1515 b'web', b'maxshortchanges', default=60,
1512 b'web', b'maxshortchanges', default=60,
1516 )
1513 )
1517 coreconfigitem(
1514 coreconfigitem(
1518 b'web', b'motd', default=b'',
1515 b'web', b'motd', default=b'',
1519 )
1516 )
1520 coreconfigitem(
1517 coreconfigitem(
1521 b'web', b'name', default=dynamicdefault,
1518 b'web', b'name', default=dynamicdefault,
1522 )
1519 )
1523 coreconfigitem(
1520 coreconfigitem(
1524 b'web', b'port', default=8000,
1521 b'web', b'port', default=8000,
1525 )
1522 )
1526 coreconfigitem(
1523 coreconfigitem(
1527 b'web', b'prefix', default=b'',
1524 b'web', b'prefix', default=b'',
1528 )
1525 )
1529 coreconfigitem(
1526 coreconfigitem(
1530 b'web', b'push_ssl', default=True,
1527 b'web', b'push_ssl', default=True,
1531 )
1528 )
1532 coreconfigitem(
1529 coreconfigitem(
1533 b'web', b'refreshinterval', default=20,
1530 b'web', b'refreshinterval', default=20,
1534 )
1531 )
1535 coreconfigitem(
1532 coreconfigitem(
1536 b'web', b'server-header', default=None,
1533 b'web', b'server-header', default=None,
1537 )
1534 )
1538 coreconfigitem(
1535 coreconfigitem(
1539 b'web', b'static', default=None,
1536 b'web', b'static', default=None,
1540 )
1537 )
1541 coreconfigitem(
1538 coreconfigitem(
1542 b'web', b'staticurl', default=None,
1539 b'web', b'staticurl', default=None,
1543 )
1540 )
1544 coreconfigitem(
1541 coreconfigitem(
1545 b'web', b'stripes', default=1,
1542 b'web', b'stripes', default=1,
1546 )
1543 )
1547 coreconfigitem(
1544 coreconfigitem(
1548 b'web', b'style', default=b'paper',
1545 b'web', b'style', default=b'paper',
1549 )
1546 )
1550 coreconfigitem(
1547 coreconfigitem(
1551 b'web', b'templates', default=None,
1548 b'web', b'templates', default=None,
1552 )
1549 )
1553 coreconfigitem(
1550 coreconfigitem(
1554 b'web', b'view', default=b'served', experimental=True,
1551 b'web', b'view', default=b'served', experimental=True,
1555 )
1552 )
1556 coreconfigitem(
1553 coreconfigitem(
1557 b'worker', b'backgroundclose', default=dynamicdefault,
1554 b'worker', b'backgroundclose', default=dynamicdefault,
1558 )
1555 )
1559 # Windows defaults to a limit of 512 open files. A buffer of 128
1556 # Windows defaults to a limit of 512 open files. A buffer of 128
1560 # should give us enough headway.
1557 # should give us enough headway.
1561 coreconfigitem(
1558 coreconfigitem(
1562 b'worker', b'backgroundclosemaxqueue', default=384,
1559 b'worker', b'backgroundclosemaxqueue', default=384,
1563 )
1560 )
1564 coreconfigitem(
1561 coreconfigitem(
1565 b'worker', b'backgroundcloseminfilecount', default=2048,
1562 b'worker', b'backgroundcloseminfilecount', default=2048,
1566 )
1563 )
1567 coreconfigitem(
1564 coreconfigitem(
1568 b'worker', b'backgroundclosethreadcount', default=4,
1565 b'worker', b'backgroundclosethreadcount', default=4,
1569 )
1566 )
1570 coreconfigitem(
1567 coreconfigitem(
1571 b'worker', b'enabled', default=True,
1568 b'worker', b'enabled', default=True,
1572 )
1569 )
1573 coreconfigitem(
1570 coreconfigitem(
1574 b'worker', b'numcpus', default=None,
1571 b'worker', b'numcpus', default=None,
1575 )
1572 )
1576
1573
1577 # Rebase related configuration moved to core because other extension are doing
1574 # Rebase related configuration moved to core because other extension are doing
1578 # strange things. For example, shelve import the extensions to reuse some bit
1575 # strange things. For example, shelve import the extensions to reuse some bit
1579 # without formally loading it.
1576 # without formally loading it.
1580 coreconfigitem(
1577 coreconfigitem(
1581 b'commands', b'rebase.requiredest', default=False,
1578 b'commands', b'rebase.requiredest', default=False,
1582 )
1579 )
1583 coreconfigitem(
1580 coreconfigitem(
1584 b'experimental', b'rebaseskipobsolete', default=True,
1581 b'experimental', b'rebaseskipobsolete', default=True,
1585 )
1582 )
1586 coreconfigitem(
1583 coreconfigitem(
1587 b'rebase', b'singletransaction', default=False,
1584 b'rebase', b'singletransaction', default=False,
1588 )
1585 )
1589 coreconfigitem(
1586 coreconfigitem(
1590 b'rebase', b'experimental.inmemory', default=False,
1587 b'rebase', b'experimental.inmemory', default=False,
1591 )
1588 )
@@ -1,80 +1,68 b''
1 The active mergestate is stored in ``.hg/merge`` when a merge is triggered
1 The active mergestate is stored in ``.hg/merge`` when a merge is triggered
2 by commands like ``hg merge``, ``hg rebase``, etc. until the merge is
2 by commands like ``hg merge``, ``hg rebase``, etc. until the merge is
3 completed or aborted to track the 3-way merge state of individual files.
3 completed or aborted to track the 3-way merge state of individual files.
4
4
5 The contents of the directory are:
5 The contents of the directory are:
6
6
7 Conflicting files
7 Conflicting files
8 -----------------
8 -----------------
9
9
10 The local version of the conflicting files are stored with their
10 The local version of the conflicting files are stored with their
11 filenames as the hash of their paths.
11 filenames as the hash of their paths.
12
12
13 state
13 state
14 -----
14 -----
15
15
16 This mergestate file record is used by hg version prior to 2.9.1
16 This mergestate file record is used by hg version prior to 2.9.1
17 and contains less data than ``state2``. If there is no contradiction
17 and contains less data than ``state2``. If there is no contradiction
18 with ``state2``, we can assume that both are written at the same time.
18 with ``state2``, we can assume that both are written at the same time.
19 In this case, data from ``state2`` is used. Otherwise, we use ``state``.
19 In this case, data from ``state2`` is used. Otherwise, we use ``state``.
20 We read/write both ``state`` and ``state2`` records to ensure backward
20 We read/write both ``state`` and ``state2`` records to ensure backward
21 compatibility.
21 compatibility.
22
22
23 state2
23 state2
24 ------
24 ------
25
25
26 This record stores a superset of data in ``state``, including new kinds
26 This record stores a superset of data in ``state``, including new kinds
27 of records in the future.
27 of records in the future.
28
28
29 Each record can contain arbitrary content and has an associated type. This
29 Each record can contain arbitrary content and has an associated type. This
30 `type` should be a letter. If `type` is uppercase, the record is mandatory:
30 `type` should be a letter. If `type` is uppercase, the record is mandatory:
31 versions of Mercurial that don't support it should abort. If `type` is
31 versions of Mercurial that don't support it should abort. If `type` is
32 lowercase, the record can be safely ignored.
32 lowercase, the record can be safely ignored.
33
33
34 Currently known records:
34 Currently known records:
35
35
36 | * L: the node of the "local" part of the merge (hexified version)
36 | * L: the node of the "local" part of the merge (hexified version)
37 | * O: the node of the "other" part of the merge (hexified version)
37 | * O: the node of the "other" part of the merge (hexified version)
38 | * F: a file to be merged entry
38 | * F: a file to be merged entry
39 | * C: a change/delete or delete/change conflict
39 | * C: a change/delete or delete/change conflict
40 | * D: a file that the external merge driver will merge internally
41 | (experimental)
42 | * P: a path conflict (file vs directory)
40 | * P: a path conflict (file vs directory)
43 | * m: the external merge driver defined for this merge plus its run state
44 | (experimental)
45 | * f: a (filename, dictionary) tuple of optional values for a given file
41 | * f: a (filename, dictionary) tuple of optional values for a given file
46 | * X: unsupported mandatory record type (used in tests)
42 | * X: unsupported mandatory record type (used in tests)
47 | * x: unsupported advisory record type (used in tests)
43 | * x: unsupported advisory record type (used in tests)
48 | * l: the labels for the parts of the merge.
44 | * l: the labels for the parts of the merge.
49
45
50 Merge driver run states (experimental):
51
52 | * u: driver-resolved files unmarked -- needs to be run next time we're
53 | about to resolve or commit
54 | * m: driver-resolved files marked -- only needs to be run before commit
55 | * s: success/skipped -- does not need to be run any more
56
57 Merge record states (indexed by filename):
46 Merge record states (indexed by filename):
58
47
59 | * u: unresolved conflict
48 | * u: unresolved conflict
60 | * r: resolved conflict
49 | * r: resolved conflict
61 | * pu: unresolved path conflict (file conflicts with directory)
50 | * pu: unresolved path conflict (file conflicts with directory)
62 | * pr: resolved path conflict
51 | * pr: resolved path conflict
63 | * d: driver-resolved conflict
64
52
65 The resolve command transitions between 'u' and 'r' for conflicts and
53 The resolve command transitions between 'u' and 'r' for conflicts and
66 'pu' and 'pr' for path conflicts.
54 'pu' and 'pr' for path conflicts.
67
55
68 This format is a list of arbitrary records of the form:
56 This format is a list of arbitrary records of the form:
69
57
70 [type][length][content]
58 [type][length][content]
71
59
72 `type` is a single character, `length` is a 4 byte integer, and
60 `type` is a single character, `length` is a 4 byte integer, and
73 `content` is an arbitrary byte sequence of length `length`.
61 `content` is an arbitrary byte sequence of length `length`.
74
62
75 Mercurial versions prior to 3.7 have a bug where if there are
63 Mercurial versions prior to 3.7 have a bug where if there are
76 unsupported mandatory merge records, attempting to clear out the merge
64 unsupported mandatory merge records, attempting to clear out the merge
77 state with hg update --clean or similar aborts. The 't' record type
65 state with hg update --clean or similar aborts. The 't' record type
78 works around that by writing out what those versions treat as an
66 works around that by writing out what those versions treat as an
79 advisory record, but later versions interpret as special: the first
67 advisory record, but later versions interpret as special: the first
80 character is the 'real' record type and everything onwards is the data.
68 character is the 'real' record type and everything onwards is the data.
@@ -1,2313 +1,2242 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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 errno
11 import errno
12 import stat
12 import stat
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 addednodeid,
17 addednodeid,
18 modifiednodeid,
18 modifiednodeid,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from .thirdparty import attr
22 from .thirdparty import attr
23 from . import (
23 from . import (
24 copies,
24 copies,
25 encoding,
25 encoding,
26 error,
26 error,
27 filemerge,
27 filemerge,
28 match as matchmod,
28 match as matchmod,
29 mergestate as mergestatemod,
29 mergestate as mergestatemod,
30 obsutil,
30 obsutil,
31 pathutil,
31 pathutil,
32 pycompat,
32 pycompat,
33 scmutil,
33 scmutil,
34 subrepoutil,
34 subrepoutil,
35 util,
35 util,
36 worker,
36 worker,
37 )
37 )
38
38
39 _pack = struct.pack
39 _pack = struct.pack
40 _unpack = struct.unpack
40 _unpack = struct.unpack
41
41
42
42
43 def _getcheckunknownconfig(repo, section, name):
43 def _getcheckunknownconfig(repo, section, name):
44 config = repo.ui.config(section, name)
44 config = repo.ui.config(section, name)
45 valid = [b'abort', b'ignore', b'warn']
45 valid = [b'abort', b'ignore', b'warn']
46 if config not in valid:
46 if config not in valid:
47 validstr = b', '.join([b"'" + v + b"'" for v in valid])
47 validstr = b', '.join([b"'" + v + b"'" for v in valid])
48 raise error.ConfigError(
48 raise error.ConfigError(
49 _(b"%s.%s not valid ('%s' is none of %s)")
49 _(b"%s.%s not valid ('%s' is none of %s)")
50 % (section, name, config, validstr)
50 % (section, name, config, validstr)
51 )
51 )
52 return config
52 return config
53
53
54
54
55 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
55 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
56 if wctx.isinmemory():
56 if wctx.isinmemory():
57 # Nothing to do in IMM because nothing in the "working copy" can be an
57 # Nothing to do in IMM because nothing in the "working copy" can be an
58 # unknown file.
58 # unknown file.
59 #
59 #
60 # Note that we should bail out here, not in ``_checkunknownfiles()``,
60 # Note that we should bail out here, not in ``_checkunknownfiles()``,
61 # because that function does other useful work.
61 # because that function does other useful work.
62 return False
62 return False
63
63
64 if f2 is None:
64 if f2 is None:
65 f2 = f
65 f2 = f
66 return (
66 return (
67 repo.wvfs.audit.check(f)
67 repo.wvfs.audit.check(f)
68 and repo.wvfs.isfileorlink(f)
68 and repo.wvfs.isfileorlink(f)
69 and repo.dirstate.normalize(f) not in repo.dirstate
69 and repo.dirstate.normalize(f) not in repo.dirstate
70 and mctx[f2].cmp(wctx[f])
70 and mctx[f2].cmp(wctx[f])
71 )
71 )
72
72
73
73
74 class _unknowndirschecker(object):
74 class _unknowndirschecker(object):
75 """
75 """
76 Look for any unknown files or directories that may have a path conflict
76 Look for any unknown files or directories that may have a path conflict
77 with a file. If any path prefix of the file exists as a file or link,
77 with a file. If any path prefix of the file exists as a file or link,
78 then it conflicts. If the file itself is a directory that contains any
78 then it conflicts. If the file itself is a directory that contains any
79 file that is not tracked, then it conflicts.
79 file that is not tracked, then it conflicts.
80
80
81 Returns the shortest path at which a conflict occurs, or None if there is
81 Returns the shortest path at which a conflict occurs, or None if there is
82 no conflict.
82 no conflict.
83 """
83 """
84
84
85 def __init__(self):
85 def __init__(self):
86 # A set of paths known to be good. This prevents repeated checking of
86 # A set of paths known to be good. This prevents repeated checking of
87 # dirs. It will be updated with any new dirs that are checked and found
87 # dirs. It will be updated with any new dirs that are checked and found
88 # to be safe.
88 # to be safe.
89 self._unknowndircache = set()
89 self._unknowndircache = set()
90
90
91 # A set of paths that are known to be absent. This prevents repeated
91 # A set of paths that are known to be absent. This prevents repeated
92 # checking of subdirectories that are known not to exist. It will be
92 # checking of subdirectories that are known not to exist. It will be
93 # updated with any new dirs that are checked and found to be absent.
93 # updated with any new dirs that are checked and found to be absent.
94 self._missingdircache = set()
94 self._missingdircache = set()
95
95
96 def __call__(self, repo, wctx, f):
96 def __call__(self, repo, wctx, f):
97 if wctx.isinmemory():
97 if wctx.isinmemory():
98 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
98 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
99 return False
99 return False
100
100
101 # Check for path prefixes that exist as unknown files.
101 # Check for path prefixes that exist as unknown files.
102 for p in reversed(list(pathutil.finddirs(f))):
102 for p in reversed(list(pathutil.finddirs(f))):
103 if p in self._missingdircache:
103 if p in self._missingdircache:
104 return
104 return
105 if p in self._unknowndircache:
105 if p in self._unknowndircache:
106 continue
106 continue
107 if repo.wvfs.audit.check(p):
107 if repo.wvfs.audit.check(p):
108 if (
108 if (
109 repo.wvfs.isfileorlink(p)
109 repo.wvfs.isfileorlink(p)
110 and repo.dirstate.normalize(p) not in repo.dirstate
110 and repo.dirstate.normalize(p) not in repo.dirstate
111 ):
111 ):
112 return p
112 return p
113 if not repo.wvfs.lexists(p):
113 if not repo.wvfs.lexists(p):
114 self._missingdircache.add(p)
114 self._missingdircache.add(p)
115 return
115 return
116 self._unknowndircache.add(p)
116 self._unknowndircache.add(p)
117
117
118 # Check if the file conflicts with a directory containing unknown files.
118 # Check if the file conflicts with a directory containing unknown files.
119 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
119 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
120 # Does the directory contain any files that are not in the dirstate?
120 # Does the directory contain any files that are not in the dirstate?
121 for p, dirs, files in repo.wvfs.walk(f):
121 for p, dirs, files in repo.wvfs.walk(f):
122 for fn in files:
122 for fn in files:
123 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
123 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
124 relf = repo.dirstate.normalize(relf, isknown=True)
124 relf = repo.dirstate.normalize(relf, isknown=True)
125 if relf not in repo.dirstate:
125 if relf not in repo.dirstate:
126 return f
126 return f
127 return None
127 return None
128
128
129
129
130 def _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce):
130 def _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce):
131 """
131 """
132 Considers any actions that care about the presence of conflicting unknown
132 Considers any actions that care about the presence of conflicting unknown
133 files. For some actions, the result is to abort; for others, it is to
133 files. For some actions, the result is to abort; for others, it is to
134 choose a different action.
134 choose a different action.
135 """
135 """
136 fileconflicts = set()
136 fileconflicts = set()
137 pathconflicts = set()
137 pathconflicts = set()
138 warnconflicts = set()
138 warnconflicts = set()
139 abortconflicts = set()
139 abortconflicts = set()
140 unknownconfig = _getcheckunknownconfig(repo, b'merge', b'checkunknown')
140 unknownconfig = _getcheckunknownconfig(repo, b'merge', b'checkunknown')
141 ignoredconfig = _getcheckunknownconfig(repo, b'merge', b'checkignored')
141 ignoredconfig = _getcheckunknownconfig(repo, b'merge', b'checkignored')
142 pathconfig = repo.ui.configbool(
142 pathconfig = repo.ui.configbool(
143 b'experimental', b'merge.checkpathconflicts'
143 b'experimental', b'merge.checkpathconflicts'
144 )
144 )
145 if not force:
145 if not force:
146
146
147 def collectconflicts(conflicts, config):
147 def collectconflicts(conflicts, config):
148 if config == b'abort':
148 if config == b'abort':
149 abortconflicts.update(conflicts)
149 abortconflicts.update(conflicts)
150 elif config == b'warn':
150 elif config == b'warn':
151 warnconflicts.update(conflicts)
151 warnconflicts.update(conflicts)
152
152
153 checkunknowndirs = _unknowndirschecker()
153 checkunknowndirs = _unknowndirschecker()
154 for f in mresult.files(
154 for f in mresult.files(
155 (
155 (
156 mergestatemod.ACTION_CREATED,
156 mergestatemod.ACTION_CREATED,
157 mergestatemod.ACTION_DELETED_CHANGED,
157 mergestatemod.ACTION_DELETED_CHANGED,
158 )
158 )
159 ):
159 ):
160 if _checkunknownfile(repo, wctx, mctx, f):
160 if _checkunknownfile(repo, wctx, mctx, f):
161 fileconflicts.add(f)
161 fileconflicts.add(f)
162 elif pathconfig and f not in wctx:
162 elif pathconfig and f not in wctx:
163 path = checkunknowndirs(repo, wctx, f)
163 path = checkunknowndirs(repo, wctx, f)
164 if path is not None:
164 if path is not None:
165 pathconflicts.add(path)
165 pathconflicts.add(path)
166 for f, args, msg in mresult.getactions(
166 for f, args, msg in mresult.getactions(
167 [mergestatemod.ACTION_LOCAL_DIR_RENAME_GET]
167 [mergestatemod.ACTION_LOCAL_DIR_RENAME_GET]
168 ):
168 ):
169 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
169 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
170 fileconflicts.add(f)
170 fileconflicts.add(f)
171
171
172 allconflicts = fileconflicts | pathconflicts
172 allconflicts = fileconflicts | pathconflicts
173 ignoredconflicts = {c for c in allconflicts if repo.dirstate._ignore(c)}
173 ignoredconflicts = {c for c in allconflicts if repo.dirstate._ignore(c)}
174 unknownconflicts = allconflicts - ignoredconflicts
174 unknownconflicts = allconflicts - ignoredconflicts
175 collectconflicts(ignoredconflicts, ignoredconfig)
175 collectconflicts(ignoredconflicts, ignoredconfig)
176 collectconflicts(unknownconflicts, unknownconfig)
176 collectconflicts(unknownconflicts, unknownconfig)
177 else:
177 else:
178 for f, args, msg in list(
178 for f, args, msg in list(
179 mresult.getactions([mergestatemod.ACTION_CREATED_MERGE])
179 mresult.getactions([mergestatemod.ACTION_CREATED_MERGE])
180 ):
180 ):
181 fl2, anc = args
181 fl2, anc = args
182 different = _checkunknownfile(repo, wctx, mctx, f)
182 different = _checkunknownfile(repo, wctx, mctx, f)
183 if repo.dirstate._ignore(f):
183 if repo.dirstate._ignore(f):
184 config = ignoredconfig
184 config = ignoredconfig
185 else:
185 else:
186 config = unknownconfig
186 config = unknownconfig
187
187
188 # The behavior when force is True is described by this table:
188 # The behavior when force is True is described by this table:
189 # config different mergeforce | action backup
189 # config different mergeforce | action backup
190 # * n * | get n
190 # * n * | get n
191 # * y y | merge -
191 # * y y | merge -
192 # abort y n | merge - (1)
192 # abort y n | merge - (1)
193 # warn y n | warn + get y
193 # warn y n | warn + get y
194 # ignore y n | get y
194 # ignore y n | get y
195 #
195 #
196 # (1) this is probably the wrong behavior here -- we should
196 # (1) this is probably the wrong behavior here -- we should
197 # probably abort, but some actions like rebases currently
197 # probably abort, but some actions like rebases currently
198 # don't like an abort happening in the middle of
198 # don't like an abort happening in the middle of
199 # merge.update.
199 # merge.update.
200 if not different:
200 if not different:
201 mresult.addfile(
201 mresult.addfile(
202 f,
202 f,
203 mergestatemod.ACTION_GET,
203 mergestatemod.ACTION_GET,
204 (fl2, False),
204 (fl2, False),
205 b'remote created',
205 b'remote created',
206 )
206 )
207 elif mergeforce or config == b'abort':
207 elif mergeforce or config == b'abort':
208 mresult.addfile(
208 mresult.addfile(
209 f,
209 f,
210 mergestatemod.ACTION_MERGE,
210 mergestatemod.ACTION_MERGE,
211 (f, f, None, False, anc),
211 (f, f, None, False, anc),
212 b'remote differs from untracked local',
212 b'remote differs from untracked local',
213 )
213 )
214 elif config == b'abort':
214 elif config == b'abort':
215 abortconflicts.add(f)
215 abortconflicts.add(f)
216 else:
216 else:
217 if config == b'warn':
217 if config == b'warn':
218 warnconflicts.add(f)
218 warnconflicts.add(f)
219 mresult.addfile(
219 mresult.addfile(
220 f, mergestatemod.ACTION_GET, (fl2, True), b'remote created',
220 f, mergestatemod.ACTION_GET, (fl2, True), b'remote created',
221 )
221 )
222
222
223 for f in sorted(abortconflicts):
223 for f in sorted(abortconflicts):
224 warn = repo.ui.warn
224 warn = repo.ui.warn
225 if f in pathconflicts:
225 if f in pathconflicts:
226 if repo.wvfs.isfileorlink(f):
226 if repo.wvfs.isfileorlink(f):
227 warn(_(b"%s: untracked file conflicts with directory\n") % f)
227 warn(_(b"%s: untracked file conflicts with directory\n") % f)
228 else:
228 else:
229 warn(_(b"%s: untracked directory conflicts with file\n") % f)
229 warn(_(b"%s: untracked directory conflicts with file\n") % f)
230 else:
230 else:
231 warn(_(b"%s: untracked file differs\n") % f)
231 warn(_(b"%s: untracked file differs\n") % f)
232 if abortconflicts:
232 if abortconflicts:
233 raise error.Abort(
233 raise error.Abort(
234 _(
234 _(
235 b"untracked files in working directory "
235 b"untracked files in working directory "
236 b"differ from files in requested revision"
236 b"differ from files in requested revision"
237 )
237 )
238 )
238 )
239
239
240 for f in sorted(warnconflicts):
240 for f in sorted(warnconflicts):
241 if repo.wvfs.isfileorlink(f):
241 if repo.wvfs.isfileorlink(f):
242 repo.ui.warn(_(b"%s: replacing untracked file\n") % f)
242 repo.ui.warn(_(b"%s: replacing untracked file\n") % f)
243 else:
243 else:
244 repo.ui.warn(_(b"%s: replacing untracked files in directory\n") % f)
244 repo.ui.warn(_(b"%s: replacing untracked files in directory\n") % f)
245
245
246 for f, args, msg in list(
246 for f, args, msg in list(
247 mresult.getactions([mergestatemod.ACTION_CREATED])
247 mresult.getactions([mergestatemod.ACTION_CREATED])
248 ):
248 ):
249 backup = (
249 backup = (
250 f in fileconflicts
250 f in fileconflicts
251 or f in pathconflicts
251 or f in pathconflicts
252 or any(p in pathconflicts for p in pathutil.finddirs(f))
252 or any(p in pathconflicts for p in pathutil.finddirs(f))
253 )
253 )
254 (flags,) = args
254 (flags,) = args
255 mresult.addfile(f, mergestatemod.ACTION_GET, (flags, backup), msg)
255 mresult.addfile(f, mergestatemod.ACTION_GET, (flags, backup), msg)
256
256
257
257
258 def _forgetremoved(wctx, mctx, branchmerge, mresult):
258 def _forgetremoved(wctx, mctx, branchmerge, mresult):
259 """
259 """
260 Forget removed files
260 Forget removed files
261
261
262 If we're jumping between revisions (as opposed to merging), and if
262 If we're jumping between revisions (as opposed to merging), and if
263 neither the working directory nor the target rev has the file,
263 neither the working directory nor the target rev has the file,
264 then we need to remove it from the dirstate, to prevent the
264 then we need to remove it from the dirstate, to prevent the
265 dirstate from listing the file when it is no longer in the
265 dirstate from listing the file when it is no longer in the
266 manifest.
266 manifest.
267
267
268 If we're merging, and the other revision has removed a file
268 If we're merging, and the other revision has removed a file
269 that is not present in the working directory, we need to mark it
269 that is not present in the working directory, we need to mark it
270 as removed.
270 as removed.
271 """
271 """
272
272
273 m = mergestatemod.ACTION_FORGET
273 m = mergestatemod.ACTION_FORGET
274 if branchmerge:
274 if branchmerge:
275 m = mergestatemod.ACTION_REMOVE
275 m = mergestatemod.ACTION_REMOVE
276 for f in wctx.deleted():
276 for f in wctx.deleted():
277 if f not in mctx:
277 if f not in mctx:
278 mresult.addfile(f, m, None, b"forget deleted")
278 mresult.addfile(f, m, None, b"forget deleted")
279
279
280 if not branchmerge:
280 if not branchmerge:
281 for f in wctx.removed():
281 for f in wctx.removed():
282 if f not in mctx:
282 if f not in mctx:
283 mresult.addfile(
283 mresult.addfile(
284 f, mergestatemod.ACTION_FORGET, None, b"forget removed",
284 f, mergestatemod.ACTION_FORGET, None, b"forget removed",
285 )
285 )
286
286
287
287
288 def _checkcollision(repo, wmf, mresult):
288 def _checkcollision(repo, wmf, mresult):
289 """
289 """
290 Check for case-folding collisions.
290 Check for case-folding collisions.
291 """
291 """
292 # If the repo is narrowed, filter out files outside the narrowspec.
292 # If the repo is narrowed, filter out files outside the narrowspec.
293 narrowmatch = repo.narrowmatch()
293 narrowmatch = repo.narrowmatch()
294 if not narrowmatch.always():
294 if not narrowmatch.always():
295 pmmf = set(wmf.walk(narrowmatch))
295 pmmf = set(wmf.walk(narrowmatch))
296 if mresult:
296 if mresult:
297 for f in list(mresult.files()):
297 for f in list(mresult.files()):
298 if not narrowmatch(f):
298 if not narrowmatch(f):
299 mresult.removefile(f)
299 mresult.removefile(f)
300 else:
300 else:
301 # build provisional merged manifest up
301 # build provisional merged manifest up
302 pmmf = set(wmf)
302 pmmf = set(wmf)
303
303
304 if mresult:
304 if mresult:
305 # KEEP and EXEC are no-op
305 # KEEP and EXEC are no-op
306 for f in mresult.files(
306 for f in mresult.files(
307 (
307 (
308 mergestatemod.ACTION_ADD,
308 mergestatemod.ACTION_ADD,
309 mergestatemod.ACTION_ADD_MODIFIED,
309 mergestatemod.ACTION_ADD_MODIFIED,
310 mergestatemod.ACTION_FORGET,
310 mergestatemod.ACTION_FORGET,
311 mergestatemod.ACTION_GET,
311 mergestatemod.ACTION_GET,
312 mergestatemod.ACTION_CHANGED_DELETED,
312 mergestatemod.ACTION_CHANGED_DELETED,
313 mergestatemod.ACTION_DELETED_CHANGED,
313 mergestatemod.ACTION_DELETED_CHANGED,
314 )
314 )
315 ):
315 ):
316 pmmf.add(f)
316 pmmf.add(f)
317 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
317 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
318 pmmf.discard(f)
318 pmmf.discard(f)
319 for f, args, msg in mresult.getactions(
319 for f, args, msg in mresult.getactions(
320 [mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL]
320 [mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL]
321 ):
321 ):
322 f2, flags = args
322 f2, flags = args
323 pmmf.discard(f2)
323 pmmf.discard(f2)
324 pmmf.add(f)
324 pmmf.add(f)
325 for f in mresult.files((mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,)):
325 for f in mresult.files((mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,)):
326 pmmf.add(f)
326 pmmf.add(f)
327 for f, args, msg in mresult.getactions([mergestatemod.ACTION_MERGE]):
327 for f, args, msg in mresult.getactions([mergestatemod.ACTION_MERGE]):
328 f1, f2, fa, move, anc = args
328 f1, f2, fa, move, anc = args
329 if move:
329 if move:
330 pmmf.discard(f1)
330 pmmf.discard(f1)
331 pmmf.add(f)
331 pmmf.add(f)
332
332
333 # check case-folding collision in provisional merged manifest
333 # check case-folding collision in provisional merged manifest
334 foldmap = {}
334 foldmap = {}
335 for f in pmmf:
335 for f in pmmf:
336 fold = util.normcase(f)
336 fold = util.normcase(f)
337 if fold in foldmap:
337 if fold in foldmap:
338 raise error.Abort(
338 raise error.Abort(
339 _(b"case-folding collision between %s and %s")
339 _(b"case-folding collision between %s and %s")
340 % (f, foldmap[fold])
340 % (f, foldmap[fold])
341 )
341 )
342 foldmap[fold] = f
342 foldmap[fold] = f
343
343
344 # check case-folding of directories
344 # check case-folding of directories
345 foldprefix = unfoldprefix = lastfull = b''
345 foldprefix = unfoldprefix = lastfull = b''
346 for fold, f in sorted(foldmap.items()):
346 for fold, f in sorted(foldmap.items()):
347 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
347 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
348 # the folded prefix matches but actual casing is different
348 # the folded prefix matches but actual casing is different
349 raise error.Abort(
349 raise error.Abort(
350 _(b"case-folding collision between %s and directory of %s")
350 _(b"case-folding collision between %s and directory of %s")
351 % (lastfull, f)
351 % (lastfull, f)
352 )
352 )
353 foldprefix = fold + b'/'
353 foldprefix = fold + b'/'
354 unfoldprefix = f + b'/'
354 unfoldprefix = f + b'/'
355 lastfull = f
355 lastfull = f
356
356
357
357
358 def driverpreprocess(repo, ms, wctx, labels=None):
359 """run the preprocess step of the merge driver, if any
360
361 This is currently not implemented -- it's an extension point."""
362 return True
363
364
365 def driverconclude(repo, ms, wctx, labels=None):
366 """run the conclude step of the merge driver, if any
367
368 This is currently not implemented -- it's an extension point."""
369 return True
370
371
372 def _filesindirs(repo, manifest, dirs):
358 def _filesindirs(repo, manifest, dirs):
373 """
359 """
374 Generator that yields pairs of all the files in the manifest that are found
360 Generator that yields pairs of all the files in the manifest that are found
375 inside the directories listed in dirs, and which directory they are found
361 inside the directories listed in dirs, and which directory they are found
376 in.
362 in.
377 """
363 """
378 for f in manifest:
364 for f in manifest:
379 for p in pathutil.finddirs(f):
365 for p in pathutil.finddirs(f):
380 if p in dirs:
366 if p in dirs:
381 yield f, p
367 yield f, p
382 break
368 break
383
369
384
370
385 def checkpathconflicts(repo, wctx, mctx, mresult):
371 def checkpathconflicts(repo, wctx, mctx, mresult):
386 """
372 """
387 Check if any actions introduce path conflicts in the repository, updating
373 Check if any actions introduce path conflicts in the repository, updating
388 actions to record or handle the path conflict accordingly.
374 actions to record or handle the path conflict accordingly.
389 """
375 """
390 mf = wctx.manifest()
376 mf = wctx.manifest()
391
377
392 # The set of local files that conflict with a remote directory.
378 # The set of local files that conflict with a remote directory.
393 localconflicts = set()
379 localconflicts = set()
394
380
395 # The set of directories that conflict with a remote file, and so may cause
381 # The set of directories that conflict with a remote file, and so may cause
396 # conflicts if they still contain any files after the merge.
382 # conflicts if they still contain any files after the merge.
397 remoteconflicts = set()
383 remoteconflicts = set()
398
384
399 # The set of directories that appear as both a file and a directory in the
385 # The set of directories that appear as both a file and a directory in the
400 # remote manifest. These indicate an invalid remote manifest, which
386 # remote manifest. These indicate an invalid remote manifest, which
401 # can't be updated to cleanly.
387 # can't be updated to cleanly.
402 invalidconflicts = set()
388 invalidconflicts = set()
403
389
404 # The set of directories that contain files that are being created.
390 # The set of directories that contain files that are being created.
405 createdfiledirs = set()
391 createdfiledirs = set()
406
392
407 # The set of files deleted by all the actions.
393 # The set of files deleted by all the actions.
408 deletedfiles = set()
394 deletedfiles = set()
409
395
410 for f in mresult.files(
396 for f in mresult.files(
411 (
397 (
412 mergestatemod.ACTION_CREATED,
398 mergestatemod.ACTION_CREATED,
413 mergestatemod.ACTION_DELETED_CHANGED,
399 mergestatemod.ACTION_DELETED_CHANGED,
414 mergestatemod.ACTION_MERGE,
400 mergestatemod.ACTION_MERGE,
415 mergestatemod.ACTION_CREATED_MERGE,
401 mergestatemod.ACTION_CREATED_MERGE,
416 )
402 )
417 ):
403 ):
418 # This action may create a new local file.
404 # This action may create a new local file.
419 createdfiledirs.update(pathutil.finddirs(f))
405 createdfiledirs.update(pathutil.finddirs(f))
420 if mf.hasdir(f):
406 if mf.hasdir(f):
421 # The file aliases a local directory. This might be ok if all
407 # The file aliases a local directory. This might be ok if all
422 # the files in the local directory are being deleted. This
408 # the files in the local directory are being deleted. This
423 # will be checked once we know what all the deleted files are.
409 # will be checked once we know what all the deleted files are.
424 remoteconflicts.add(f)
410 remoteconflicts.add(f)
425 # Track the names of all deleted files.
411 # Track the names of all deleted files.
426 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
412 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
427 deletedfiles.add(f)
413 deletedfiles.add(f)
428 for (f, args, msg) in mresult.getactions((mergestatemod.ACTION_MERGE,)):
414 for (f, args, msg) in mresult.getactions((mergestatemod.ACTION_MERGE,)):
429 f1, f2, fa, move, anc = args
415 f1, f2, fa, move, anc = args
430 if move:
416 if move:
431 deletedfiles.add(f1)
417 deletedfiles.add(f1)
432 for (f, args, msg) in mresult.getactions(
418 for (f, args, msg) in mresult.getactions(
433 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,)
419 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,)
434 ):
420 ):
435 f2, flags = args
421 f2, flags = args
436 deletedfiles.add(f2)
422 deletedfiles.add(f2)
437
423
438 # Check all directories that contain created files for path conflicts.
424 # Check all directories that contain created files for path conflicts.
439 for p in createdfiledirs:
425 for p in createdfiledirs:
440 if p in mf:
426 if p in mf:
441 if p in mctx:
427 if p in mctx:
442 # A file is in a directory which aliases both a local
428 # A file is in a directory which aliases both a local
443 # and a remote file. This is an internal inconsistency
429 # and a remote file. This is an internal inconsistency
444 # within the remote manifest.
430 # within the remote manifest.
445 invalidconflicts.add(p)
431 invalidconflicts.add(p)
446 else:
432 else:
447 # A file is in a directory which aliases a local file.
433 # A file is in a directory which aliases a local file.
448 # We will need to rename the local file.
434 # We will need to rename the local file.
449 localconflicts.add(p)
435 localconflicts.add(p)
450 pd = mresult.getfile(p)
436 pd = mresult.getfile(p)
451 if pd and pd[0] in (
437 if pd and pd[0] in (
452 mergestatemod.ACTION_CREATED,
438 mergestatemod.ACTION_CREATED,
453 mergestatemod.ACTION_DELETED_CHANGED,
439 mergestatemod.ACTION_DELETED_CHANGED,
454 mergestatemod.ACTION_MERGE,
440 mergestatemod.ACTION_MERGE,
455 mergestatemod.ACTION_CREATED_MERGE,
441 mergestatemod.ACTION_CREATED_MERGE,
456 ):
442 ):
457 # The file is in a directory which aliases a remote file.
443 # The file is in a directory which aliases a remote file.
458 # This is an internal inconsistency within the remote
444 # This is an internal inconsistency within the remote
459 # manifest.
445 # manifest.
460 invalidconflicts.add(p)
446 invalidconflicts.add(p)
461
447
462 # Rename all local conflicting files that have not been deleted.
448 # Rename all local conflicting files that have not been deleted.
463 for p in localconflicts:
449 for p in localconflicts:
464 if p not in deletedfiles:
450 if p not in deletedfiles:
465 ctxname = bytes(wctx).rstrip(b'+')
451 ctxname = bytes(wctx).rstrip(b'+')
466 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
452 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
467 porig = wctx[p].copysource() or p
453 porig = wctx[p].copysource() or p
468 mresult.addfile(
454 mresult.addfile(
469 pnew,
455 pnew,
470 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
456 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
471 (p, porig),
457 (p, porig),
472 b'local path conflict',
458 b'local path conflict',
473 )
459 )
474 mresult.addfile(
460 mresult.addfile(
475 p,
461 p,
476 mergestatemod.ACTION_PATH_CONFLICT,
462 mergestatemod.ACTION_PATH_CONFLICT,
477 (pnew, b'l'),
463 (pnew, b'l'),
478 b'path conflict',
464 b'path conflict',
479 )
465 )
480
466
481 if remoteconflicts:
467 if remoteconflicts:
482 # Check if all files in the conflicting directories have been removed.
468 # Check if all files in the conflicting directories have been removed.
483 ctxname = bytes(mctx).rstrip(b'+')
469 ctxname = bytes(mctx).rstrip(b'+')
484 for f, p in _filesindirs(repo, mf, remoteconflicts):
470 for f, p in _filesindirs(repo, mf, remoteconflicts):
485 if f not in deletedfiles:
471 if f not in deletedfiles:
486 m, args, msg = mresult.getfile(p)
472 m, args, msg = mresult.getfile(p)
487 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
473 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
488 if m in (
474 if m in (
489 mergestatemod.ACTION_DELETED_CHANGED,
475 mergestatemod.ACTION_DELETED_CHANGED,
490 mergestatemod.ACTION_MERGE,
476 mergestatemod.ACTION_MERGE,
491 ):
477 ):
492 # Action was merge, just update target.
478 # Action was merge, just update target.
493 mresult.addfile(pnew, m, args, msg)
479 mresult.addfile(pnew, m, args, msg)
494 else:
480 else:
495 # Action was create, change to renamed get action.
481 # Action was create, change to renamed get action.
496 fl = args[0]
482 fl = args[0]
497 mresult.addfile(
483 mresult.addfile(
498 pnew,
484 pnew,
499 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
485 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
500 (p, fl),
486 (p, fl),
501 b'remote path conflict',
487 b'remote path conflict',
502 )
488 )
503 mresult.addfile(
489 mresult.addfile(
504 p,
490 p,
505 mergestatemod.ACTION_PATH_CONFLICT,
491 mergestatemod.ACTION_PATH_CONFLICT,
506 (pnew, mergestatemod.ACTION_REMOVE),
492 (pnew, mergestatemod.ACTION_REMOVE),
507 b'path conflict',
493 b'path conflict',
508 )
494 )
509 remoteconflicts.remove(p)
495 remoteconflicts.remove(p)
510 break
496 break
511
497
512 if invalidconflicts:
498 if invalidconflicts:
513 for p in invalidconflicts:
499 for p in invalidconflicts:
514 repo.ui.warn(_(b"%s: is both a file and a directory\n") % p)
500 repo.ui.warn(_(b"%s: is both a file and a directory\n") % p)
515 raise error.Abort(_(b"destination manifest contains path conflicts"))
501 raise error.Abort(_(b"destination manifest contains path conflicts"))
516
502
517
503
518 def _filternarrowactions(narrowmatch, branchmerge, mresult):
504 def _filternarrowactions(narrowmatch, branchmerge, mresult):
519 """
505 """
520 Filters out actions that can ignored because the repo is narrowed.
506 Filters out actions that can ignored because the repo is narrowed.
521
507
522 Raise an exception if the merge cannot be completed because the repo is
508 Raise an exception if the merge cannot be completed because the repo is
523 narrowed.
509 narrowed.
524 """
510 """
525 # TODO: handle with nonconflicttypes
511 # TODO: handle with nonconflicttypes
526 nonconflicttypes = {
512 nonconflicttypes = {
527 mergestatemod.ACTION_ADD,
513 mergestatemod.ACTION_ADD,
528 mergestatemod.ACTION_ADD_MODIFIED,
514 mergestatemod.ACTION_ADD_MODIFIED,
529 mergestatemod.ACTION_CREATED,
515 mergestatemod.ACTION_CREATED,
530 mergestatemod.ACTION_CREATED_MERGE,
516 mergestatemod.ACTION_CREATED_MERGE,
531 mergestatemod.ACTION_FORGET,
517 mergestatemod.ACTION_FORGET,
532 mergestatemod.ACTION_GET,
518 mergestatemod.ACTION_GET,
533 mergestatemod.ACTION_REMOVE,
519 mergestatemod.ACTION_REMOVE,
534 mergestatemod.ACTION_EXEC,
520 mergestatemod.ACTION_EXEC,
535 }
521 }
536 # We mutate the items in the dict during iteration, so iterate
522 # We mutate the items in the dict during iteration, so iterate
537 # over a copy.
523 # over a copy.
538 for f, action in mresult.filemap():
524 for f, action in mresult.filemap():
539 if narrowmatch(f):
525 if narrowmatch(f):
540 pass
526 pass
541 elif not branchmerge:
527 elif not branchmerge:
542 mresult.removefile(f) # just updating, ignore changes outside clone
528 mresult.removefile(f) # just updating, ignore changes outside clone
543 elif action[0] in mergeresult.NO_OP_ACTIONS:
529 elif action[0] in mergeresult.NO_OP_ACTIONS:
544 mresult.removefile(f) # merge does not affect file
530 mresult.removefile(f) # merge does not affect file
545 elif action[0] in nonconflicttypes:
531 elif action[0] in nonconflicttypes:
546 raise error.Abort(
532 raise error.Abort(
547 _(
533 _(
548 b'merge affects file \'%s\' outside narrow, '
534 b'merge affects file \'%s\' outside narrow, '
549 b'which is not yet supported'
535 b'which is not yet supported'
550 )
536 )
551 % f,
537 % f,
552 hint=_(b'merging in the other direction may work'),
538 hint=_(b'merging in the other direction may work'),
553 )
539 )
554 else:
540 else:
555 raise error.Abort(
541 raise error.Abort(
556 _(b'conflict in file \'%s\' is outside narrow clone') % f
542 _(b'conflict in file \'%s\' is outside narrow clone') % f
557 )
543 )
558
544
559
545
560 class mergeresult(object):
546 class mergeresult(object):
561 ''''An object representing result of merging manifests.
547 ''''An object representing result of merging manifests.
562
548
563 It has information about what actions need to be performed on dirstate
549 It has information about what actions need to be performed on dirstate
564 mapping of divergent renames and other such cases. '''
550 mapping of divergent renames and other such cases. '''
565
551
566 NO_OP_ACTIONS = (
552 NO_OP_ACTIONS = (
567 mergestatemod.ACTION_KEEP,
553 mergestatemod.ACTION_KEEP,
568 mergestatemod.ACTION_KEEP_ABSENT,
554 mergestatemod.ACTION_KEEP_ABSENT,
569 )
555 )
570
556
571 def __init__(self):
557 def __init__(self):
572 """
558 """
573 filemapping: dict of filename as keys and action related info as values
559 filemapping: dict of filename as keys and action related info as values
574 diverge: mapping of source name -> list of dest name for
560 diverge: mapping of source name -> list of dest name for
575 divergent renames
561 divergent renames
576 renamedelete: mapping of source name -> list of destinations for files
562 renamedelete: mapping of source name -> list of destinations for files
577 deleted on one side and renamed on other.
563 deleted on one side and renamed on other.
578 commitinfo: dict containing data which should be used on commit
564 commitinfo: dict containing data which should be used on commit
579 contains a filename -> info mapping
565 contains a filename -> info mapping
580 actionmapping: dict of action names as keys and values are dict of
566 actionmapping: dict of action names as keys and values are dict of
581 filename as key and related data as values
567 filename as key and related data as values
582 """
568 """
583 self._filemapping = {}
569 self._filemapping = {}
584 self._diverge = {}
570 self._diverge = {}
585 self._renamedelete = {}
571 self._renamedelete = {}
586 self._commitinfo = collections.defaultdict(dict)
572 self._commitinfo = collections.defaultdict(dict)
587 self._actionmapping = collections.defaultdict(dict)
573 self._actionmapping = collections.defaultdict(dict)
588
574
589 def updatevalues(self, diverge, renamedelete):
575 def updatevalues(self, diverge, renamedelete):
590 self._diverge = diverge
576 self._diverge = diverge
591 self._renamedelete = renamedelete
577 self._renamedelete = renamedelete
592
578
593 def addfile(self, filename, action, data, message):
579 def addfile(self, filename, action, data, message):
594 """ adds a new file to the mergeresult object
580 """ adds a new file to the mergeresult object
595
581
596 filename: file which we are adding
582 filename: file which we are adding
597 action: one of mergestatemod.ACTION_*
583 action: one of mergestatemod.ACTION_*
598 data: a tuple of information like fctx and ctx related to this merge
584 data: a tuple of information like fctx and ctx related to this merge
599 message: a message about the merge
585 message: a message about the merge
600 """
586 """
601 # if the file already existed, we need to delete it's old
587 # if the file already existed, we need to delete it's old
602 # entry form _actionmapping too
588 # entry form _actionmapping too
603 if filename in self._filemapping:
589 if filename in self._filemapping:
604 a, d, m = self._filemapping[filename]
590 a, d, m = self._filemapping[filename]
605 del self._actionmapping[a][filename]
591 del self._actionmapping[a][filename]
606
592
607 self._filemapping[filename] = (action, data, message)
593 self._filemapping[filename] = (action, data, message)
608 self._actionmapping[action][filename] = (data, message)
594 self._actionmapping[action][filename] = (data, message)
609
595
610 def getfile(self, filename, default_return=None):
596 def getfile(self, filename, default_return=None):
611 """ returns (action, args, msg) about this file
597 """ returns (action, args, msg) about this file
612
598
613 returns default_return if the file is not present """
599 returns default_return if the file is not present """
614 if filename in self._filemapping:
600 if filename in self._filemapping:
615 return self._filemapping[filename]
601 return self._filemapping[filename]
616 return default_return
602 return default_return
617
603
618 def files(self, actions=None):
604 def files(self, actions=None):
619 """ returns files on which provided action needs to perfromed
605 """ returns files on which provided action needs to perfromed
620
606
621 If actions is None, all files are returned
607 If actions is None, all files are returned
622 """
608 """
623 # TODO: think whether we should return renamedelete and
609 # TODO: think whether we should return renamedelete and
624 # diverge filenames also
610 # diverge filenames also
625 if actions is None:
611 if actions is None:
626 for f in self._filemapping:
612 for f in self._filemapping:
627 yield f
613 yield f
628
614
629 else:
615 else:
630 for a in actions:
616 for a in actions:
631 for f in self._actionmapping[a]:
617 for f in self._actionmapping[a]:
632 yield f
618 yield f
633
619
634 def removefile(self, filename):
620 def removefile(self, filename):
635 """ removes a file from the mergeresult object as the file might
621 """ removes a file from the mergeresult object as the file might
636 not merging anymore """
622 not merging anymore """
637 action, data, message = self._filemapping[filename]
623 action, data, message = self._filemapping[filename]
638 del self._filemapping[filename]
624 del self._filemapping[filename]
639 del self._actionmapping[action][filename]
625 del self._actionmapping[action][filename]
640
626
641 def getactions(self, actions, sort=False):
627 def getactions(self, actions, sort=False):
642 """ get list of files which are marked with these actions
628 """ get list of files which are marked with these actions
643 if sort is true, files for each action is sorted and then added
629 if sort is true, files for each action is sorted and then added
644
630
645 Returns a list of tuple of form (filename, data, message)
631 Returns a list of tuple of form (filename, data, message)
646 """
632 """
647 for a in actions:
633 for a in actions:
648 if sort:
634 if sort:
649 for f in sorted(self._actionmapping[a]):
635 for f in sorted(self._actionmapping[a]):
650 args, msg = self._actionmapping[a][f]
636 args, msg = self._actionmapping[a][f]
651 yield f, args, msg
637 yield f, args, msg
652 else:
638 else:
653 for f, (args, msg) in pycompat.iteritems(
639 for f, (args, msg) in pycompat.iteritems(
654 self._actionmapping[a]
640 self._actionmapping[a]
655 ):
641 ):
656 yield f, args, msg
642 yield f, args, msg
657
643
658 def len(self, actions=None):
644 def len(self, actions=None):
659 """ returns number of files which needs actions
645 """ returns number of files which needs actions
660
646
661 if actions is passed, total of number of files in that action
647 if actions is passed, total of number of files in that action
662 only is returned """
648 only is returned """
663
649
664 if actions is None:
650 if actions is None:
665 return len(self._filemapping)
651 return len(self._filemapping)
666
652
667 return sum(len(self._actionmapping[a]) for a in actions)
653 return sum(len(self._actionmapping[a]) for a in actions)
668
654
669 def filemap(self, sort=False):
655 def filemap(self, sort=False):
670 if sorted:
656 if sorted:
671 for key, val in sorted(pycompat.iteritems(self._filemapping)):
657 for key, val in sorted(pycompat.iteritems(self._filemapping)):
672 yield key, val
658 yield key, val
673 else:
659 else:
674 for key, val in pycompat.iteritems(self._filemapping):
660 for key, val in pycompat.iteritems(self._filemapping):
675 yield key, val
661 yield key, val
676
662
677 def addcommitinfo(self, filename, key, value):
663 def addcommitinfo(self, filename, key, value):
678 """ adds key-value information about filename which will be required
664 """ adds key-value information about filename which will be required
679 while committing this merge """
665 while committing this merge """
680 self._commitinfo[filename][key] = value
666 self._commitinfo[filename][key] = value
681
667
682 @property
668 @property
683 def diverge(self):
669 def diverge(self):
684 return self._diverge
670 return self._diverge
685
671
686 @property
672 @property
687 def renamedelete(self):
673 def renamedelete(self):
688 return self._renamedelete
674 return self._renamedelete
689
675
690 @property
676 @property
691 def commitinfo(self):
677 def commitinfo(self):
692 return self._commitinfo
678 return self._commitinfo
693
679
694 @property
680 @property
695 def actionsdict(self):
681 def actionsdict(self):
696 """ returns a dictionary of actions to be perfomed with action as key
682 """ returns a dictionary of actions to be perfomed with action as key
697 and a list of files and related arguments as values """
683 and a list of files and related arguments as values """
698 res = collections.defaultdict(list)
684 res = collections.defaultdict(list)
699 for a, d in pycompat.iteritems(self._actionmapping):
685 for a, d in pycompat.iteritems(self._actionmapping):
700 for f, (args, msg) in pycompat.iteritems(d):
686 for f, (args, msg) in pycompat.iteritems(d):
701 res[a].append((f, args, msg))
687 res[a].append((f, args, msg))
702 return res
688 return res
703
689
704 def setactions(self, actions):
690 def setactions(self, actions):
705 self._filemapping = actions
691 self._filemapping = actions
706 self._actionmapping = collections.defaultdict(dict)
692 self._actionmapping = collections.defaultdict(dict)
707 for f, (act, data, msg) in pycompat.iteritems(self._filemapping):
693 for f, (act, data, msg) in pycompat.iteritems(self._filemapping):
708 self._actionmapping[act][f] = data, msg
694 self._actionmapping[act][f] = data, msg
709
695
710 def hasconflicts(self):
696 def hasconflicts(self):
711 """ tells whether this merge resulted in some actions which can
697 """ tells whether this merge resulted in some actions which can
712 result in conflicts or not """
698 result in conflicts or not """
713 for a in self._actionmapping.keys():
699 for a in self._actionmapping.keys():
714 if (
700 if (
715 a
701 a
716 not in (
702 not in (
717 mergestatemod.ACTION_GET,
703 mergestatemod.ACTION_GET,
718 mergestatemod.ACTION_EXEC,
704 mergestatemod.ACTION_EXEC,
719 mergestatemod.ACTION_REMOVE,
705 mergestatemod.ACTION_REMOVE,
720 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
706 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
721 )
707 )
722 and self._actionmapping[a]
708 and self._actionmapping[a]
723 and a not in self.NO_OP_ACTIONS
709 and a not in self.NO_OP_ACTIONS
724 ):
710 ):
725 return True
711 return True
726
712
727 return False
713 return False
728
714
729
715
730 def manifestmerge(
716 def manifestmerge(
731 repo,
717 repo,
732 wctx,
718 wctx,
733 p2,
719 p2,
734 pa,
720 pa,
735 branchmerge,
721 branchmerge,
736 force,
722 force,
737 matcher,
723 matcher,
738 acceptremote,
724 acceptremote,
739 followcopies,
725 followcopies,
740 forcefulldiff=False,
726 forcefulldiff=False,
741 ):
727 ):
742 """
728 """
743 Merge wctx and p2 with ancestor pa and generate merge action list
729 Merge wctx and p2 with ancestor pa and generate merge action list
744
730
745 branchmerge and force are as passed in to update
731 branchmerge and force are as passed in to update
746 matcher = matcher to filter file lists
732 matcher = matcher to filter file lists
747 acceptremote = accept the incoming changes without prompting
733 acceptremote = accept the incoming changes without prompting
748
734
749 Returns an object of mergeresult class
735 Returns an object of mergeresult class
750 """
736 """
751 mresult = mergeresult()
737 mresult = mergeresult()
752 if matcher is not None and matcher.always():
738 if matcher is not None and matcher.always():
753 matcher = None
739 matcher = None
754
740
755 # manifests fetched in order are going to be faster, so prime the caches
741 # manifests fetched in order are going to be faster, so prime the caches
756 [
742 [
757 x.manifest()
743 x.manifest()
758 for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
744 for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
759 ]
745 ]
760
746
761 branch_copies1 = copies.branch_copies()
747 branch_copies1 = copies.branch_copies()
762 branch_copies2 = copies.branch_copies()
748 branch_copies2 = copies.branch_copies()
763 diverge = {}
749 diverge = {}
764 # information from merge which is needed at commit time
750 # information from merge which is needed at commit time
765 # for example choosing filelog of which parent to commit
751 # for example choosing filelog of which parent to commit
766 # TODO: use specific constants in future for this mapping
752 # TODO: use specific constants in future for this mapping
767 if followcopies:
753 if followcopies:
768 branch_copies1, branch_copies2, diverge = copies.mergecopies(
754 branch_copies1, branch_copies2, diverge = copies.mergecopies(
769 repo, wctx, p2, pa
755 repo, wctx, p2, pa
770 )
756 )
771
757
772 boolbm = pycompat.bytestr(bool(branchmerge))
758 boolbm = pycompat.bytestr(bool(branchmerge))
773 boolf = pycompat.bytestr(bool(force))
759 boolf = pycompat.bytestr(bool(force))
774 boolm = pycompat.bytestr(bool(matcher))
760 boolm = pycompat.bytestr(bool(matcher))
775 repo.ui.note(_(b"resolving manifests\n"))
761 repo.ui.note(_(b"resolving manifests\n"))
776 repo.ui.debug(
762 repo.ui.debug(
777 b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
763 b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
778 )
764 )
779 repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
765 repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
780
766
781 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
767 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
782 copied1 = set(branch_copies1.copy.values())
768 copied1 = set(branch_copies1.copy.values())
783 copied1.update(branch_copies1.movewithdir.values())
769 copied1.update(branch_copies1.movewithdir.values())
784 copied2 = set(branch_copies2.copy.values())
770 copied2 = set(branch_copies2.copy.values())
785 copied2.update(branch_copies2.movewithdir.values())
771 copied2.update(branch_copies2.movewithdir.values())
786
772
787 if b'.hgsubstate' in m1 and wctx.rev() is None:
773 if b'.hgsubstate' in m1 and wctx.rev() is None:
788 # Check whether sub state is modified, and overwrite the manifest
774 # Check whether sub state is modified, and overwrite the manifest
789 # to flag the change. If wctx is a committed revision, we shouldn't
775 # to flag the change. If wctx is a committed revision, we shouldn't
790 # care for the dirty state of the working directory.
776 # care for the dirty state of the working directory.
791 if any(wctx.sub(s).dirty() for s in wctx.substate):
777 if any(wctx.sub(s).dirty() for s in wctx.substate):
792 m1[b'.hgsubstate'] = modifiednodeid
778 m1[b'.hgsubstate'] = modifiednodeid
793
779
794 # Don't use m2-vs-ma optimization if:
780 # Don't use m2-vs-ma optimization if:
795 # - ma is the same as m1 or m2, which we're just going to diff again later
781 # - ma is the same as m1 or m2, which we're just going to diff again later
796 # - The caller specifically asks for a full diff, which is useful during bid
782 # - The caller specifically asks for a full diff, which is useful during bid
797 # merge.
783 # merge.
798 if pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff:
784 if pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff:
799 # Identify which files are relevant to the merge, so we can limit the
785 # Identify which files are relevant to the merge, so we can limit the
800 # total m1-vs-m2 diff to just those files. This has significant
786 # total m1-vs-m2 diff to just those files. This has significant
801 # performance benefits in large repositories.
787 # performance benefits in large repositories.
802 relevantfiles = set(ma.diff(m2).keys())
788 relevantfiles = set(ma.diff(m2).keys())
803
789
804 # For copied and moved files, we need to add the source file too.
790 # For copied and moved files, we need to add the source file too.
805 for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
791 for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
806 if copyvalue in relevantfiles:
792 if copyvalue in relevantfiles:
807 relevantfiles.add(copykey)
793 relevantfiles.add(copykey)
808 for movedirkey in branch_copies1.movewithdir:
794 for movedirkey in branch_copies1.movewithdir:
809 relevantfiles.add(movedirkey)
795 relevantfiles.add(movedirkey)
810 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
796 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
811 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
797 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
812
798
813 diff = m1.diff(m2, match=matcher)
799 diff = m1.diff(m2, match=matcher)
814
800
815 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
801 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
816 if n1 and n2: # file exists on both local and remote side
802 if n1 and n2: # file exists on both local and remote side
817 if f not in ma:
803 if f not in ma:
818 # TODO: what if they're renamed from different sources?
804 # TODO: what if they're renamed from different sources?
819 fa = branch_copies1.copy.get(
805 fa = branch_copies1.copy.get(
820 f, None
806 f, None
821 ) or branch_copies2.copy.get(f, None)
807 ) or branch_copies2.copy.get(f, None)
822 args, msg = None, None
808 args, msg = None, None
823 if fa is not None:
809 if fa is not None:
824 args = (f, f, fa, False, pa.node())
810 args = (f, f, fa, False, pa.node())
825 msg = b'both renamed from %s' % fa
811 msg = b'both renamed from %s' % fa
826 else:
812 else:
827 args = (f, f, None, False, pa.node())
813 args = (f, f, None, False, pa.node())
828 msg = b'both created'
814 msg = b'both created'
829 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
815 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
830 else:
816 else:
831 a = ma[f]
817 a = ma[f]
832 fla = ma.flags(f)
818 fla = ma.flags(f)
833 nol = b'l' not in fl1 + fl2 + fla
819 nol = b'l' not in fl1 + fl2 + fla
834 if n2 == a and fl2 == fla:
820 if n2 == a and fl2 == fla:
835 mresult.addfile(
821 mresult.addfile(
836 f, mergestatemod.ACTION_KEEP, (), b'remote unchanged',
822 f, mergestatemod.ACTION_KEEP, (), b'remote unchanged',
837 )
823 )
838 elif n1 == a and fl1 == fla: # local unchanged - use remote
824 elif n1 == a and fl1 == fla: # local unchanged - use remote
839 if n1 == n2: # optimization: keep local content
825 if n1 == n2: # optimization: keep local content
840 mresult.addfile(
826 mresult.addfile(
841 f,
827 f,
842 mergestatemod.ACTION_EXEC,
828 mergestatemod.ACTION_EXEC,
843 (fl2,),
829 (fl2,),
844 b'update permissions',
830 b'update permissions',
845 )
831 )
846 else:
832 else:
847 mresult.addfile(
833 mresult.addfile(
848 f,
834 f,
849 mergestatemod.ACTION_GET,
835 mergestatemod.ACTION_GET,
850 (fl2, False),
836 (fl2, False),
851 b'remote is newer',
837 b'remote is newer',
852 )
838 )
853 if branchmerge:
839 if branchmerge:
854 mresult.addcommitinfo(
840 mresult.addcommitinfo(
855 f, b'filenode-source', b'other'
841 f, b'filenode-source', b'other'
856 )
842 )
857 elif nol and n2 == a: # remote only changed 'x'
843 elif nol and n2 == a: # remote only changed 'x'
858 mresult.addfile(
844 mresult.addfile(
859 f,
845 f,
860 mergestatemod.ACTION_EXEC,
846 mergestatemod.ACTION_EXEC,
861 (fl2,),
847 (fl2,),
862 b'update permissions',
848 b'update permissions',
863 )
849 )
864 elif nol and n1 == a: # local only changed 'x'
850 elif nol and n1 == a: # local only changed 'x'
865 mresult.addfile(
851 mresult.addfile(
866 f,
852 f,
867 mergestatemod.ACTION_GET,
853 mergestatemod.ACTION_GET,
868 (fl1, False),
854 (fl1, False),
869 b'remote is newer',
855 b'remote is newer',
870 )
856 )
871 if branchmerge:
857 if branchmerge:
872 mresult.addcommitinfo(f, b'filenode-source', b'other')
858 mresult.addcommitinfo(f, b'filenode-source', b'other')
873 else: # both changed something
859 else: # both changed something
874 mresult.addfile(
860 mresult.addfile(
875 f,
861 f,
876 mergestatemod.ACTION_MERGE,
862 mergestatemod.ACTION_MERGE,
877 (f, f, f, False, pa.node()),
863 (f, f, f, False, pa.node()),
878 b'versions differ',
864 b'versions differ',
879 )
865 )
880 elif n1: # file exists only on local side
866 elif n1: # file exists only on local side
881 if f in copied2:
867 if f in copied2:
882 pass # we'll deal with it on m2 side
868 pass # we'll deal with it on m2 side
883 elif (
869 elif (
884 f in branch_copies1.movewithdir
870 f in branch_copies1.movewithdir
885 ): # directory rename, move local
871 ): # directory rename, move local
886 f2 = branch_copies1.movewithdir[f]
872 f2 = branch_copies1.movewithdir[f]
887 if f2 in m2:
873 if f2 in m2:
888 mresult.addfile(
874 mresult.addfile(
889 f2,
875 f2,
890 mergestatemod.ACTION_MERGE,
876 mergestatemod.ACTION_MERGE,
891 (f, f2, None, True, pa.node()),
877 (f, f2, None, True, pa.node()),
892 b'remote directory rename, both created',
878 b'remote directory rename, both created',
893 )
879 )
894 else:
880 else:
895 mresult.addfile(
881 mresult.addfile(
896 f2,
882 f2,
897 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
883 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
898 (f, fl1),
884 (f, fl1),
899 b'remote directory rename - move from %s' % f,
885 b'remote directory rename - move from %s' % f,
900 )
886 )
901 elif f in branch_copies1.copy:
887 elif f in branch_copies1.copy:
902 f2 = branch_copies1.copy[f]
888 f2 = branch_copies1.copy[f]
903 mresult.addfile(
889 mresult.addfile(
904 f,
890 f,
905 mergestatemod.ACTION_MERGE,
891 mergestatemod.ACTION_MERGE,
906 (f, f2, f2, False, pa.node()),
892 (f, f2, f2, False, pa.node()),
907 b'local copied/moved from %s' % f2,
893 b'local copied/moved from %s' % f2,
908 )
894 )
909 elif f in ma: # clean, a different, no remote
895 elif f in ma: # clean, a different, no remote
910 if n1 != ma[f]:
896 if n1 != ma[f]:
911 if acceptremote:
897 if acceptremote:
912 mresult.addfile(
898 mresult.addfile(
913 f,
899 f,
914 mergestatemod.ACTION_REMOVE,
900 mergestatemod.ACTION_REMOVE,
915 None,
901 None,
916 b'remote delete',
902 b'remote delete',
917 )
903 )
918 else:
904 else:
919 mresult.addfile(
905 mresult.addfile(
920 f,
906 f,
921 mergestatemod.ACTION_CHANGED_DELETED,
907 mergestatemod.ACTION_CHANGED_DELETED,
922 (f, None, f, False, pa.node()),
908 (f, None, f, False, pa.node()),
923 b'prompt changed/deleted',
909 b'prompt changed/deleted',
924 )
910 )
925 elif n1 == addednodeid:
911 elif n1 == addednodeid:
926 # This file was locally added. We should forget it instead of
912 # This file was locally added. We should forget it instead of
927 # deleting it.
913 # deleting it.
928 mresult.addfile(
914 mresult.addfile(
929 f, mergestatemod.ACTION_FORGET, None, b'remote deleted',
915 f, mergestatemod.ACTION_FORGET, None, b'remote deleted',
930 )
916 )
931 else:
917 else:
932 mresult.addfile(
918 mresult.addfile(
933 f, mergestatemod.ACTION_REMOVE, None, b'other deleted',
919 f, mergestatemod.ACTION_REMOVE, None, b'other deleted',
934 )
920 )
935 else: # file not in ancestor, not in remote
921 else: # file not in ancestor, not in remote
936 mresult.addfile(
922 mresult.addfile(
937 f,
923 f,
938 mergestatemod.ACTION_KEEP,
924 mergestatemod.ACTION_KEEP,
939 None,
925 None,
940 b'ancestor missing, remote missing',
926 b'ancestor missing, remote missing',
941 )
927 )
942
928
943 elif n2: # file exists only on remote side
929 elif n2: # file exists only on remote side
944 if f in copied1:
930 if f in copied1:
945 pass # we'll deal with it on m1 side
931 pass # we'll deal with it on m1 side
946 elif f in branch_copies2.movewithdir:
932 elif f in branch_copies2.movewithdir:
947 f2 = branch_copies2.movewithdir[f]
933 f2 = branch_copies2.movewithdir[f]
948 if f2 in m1:
934 if f2 in m1:
949 mresult.addfile(
935 mresult.addfile(
950 f2,
936 f2,
951 mergestatemod.ACTION_MERGE,
937 mergestatemod.ACTION_MERGE,
952 (f2, f, None, False, pa.node()),
938 (f2, f, None, False, pa.node()),
953 b'local directory rename, both created',
939 b'local directory rename, both created',
954 )
940 )
955 else:
941 else:
956 mresult.addfile(
942 mresult.addfile(
957 f2,
943 f2,
958 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
944 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
959 (f, fl2),
945 (f, fl2),
960 b'local directory rename - get from %s' % f,
946 b'local directory rename - get from %s' % f,
961 )
947 )
962 elif f in branch_copies2.copy:
948 elif f in branch_copies2.copy:
963 f2 = branch_copies2.copy[f]
949 f2 = branch_copies2.copy[f]
964 msg, args = None, None
950 msg, args = None, None
965 if f2 in m2:
951 if f2 in m2:
966 args = (f2, f, f2, False, pa.node())
952 args = (f2, f, f2, False, pa.node())
967 msg = b'remote copied from %s' % f2
953 msg = b'remote copied from %s' % f2
968 else:
954 else:
969 args = (f2, f, f2, True, pa.node())
955 args = (f2, f, f2, True, pa.node())
970 msg = b'remote moved from %s' % f2
956 msg = b'remote moved from %s' % f2
971 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
957 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
972 elif f not in ma:
958 elif f not in ma:
973 # local unknown, remote created: the logic is described by the
959 # local unknown, remote created: the logic is described by the
974 # following table:
960 # following table:
975 #
961 #
976 # force branchmerge different | action
962 # force branchmerge different | action
977 # n * * | create
963 # n * * | create
978 # y n * | create
964 # y n * | create
979 # y y n | create
965 # y y n | create
980 # y y y | merge
966 # y y y | merge
981 #
967 #
982 # Checking whether the files are different is expensive, so we
968 # Checking whether the files are different is expensive, so we
983 # don't do that when we can avoid it.
969 # don't do that when we can avoid it.
984 if not force:
970 if not force:
985 mresult.addfile(
971 mresult.addfile(
986 f,
972 f,
987 mergestatemod.ACTION_CREATED,
973 mergestatemod.ACTION_CREATED,
988 (fl2,),
974 (fl2,),
989 b'remote created',
975 b'remote created',
990 )
976 )
991 elif not branchmerge:
977 elif not branchmerge:
992 mresult.addfile(
978 mresult.addfile(
993 f,
979 f,
994 mergestatemod.ACTION_CREATED,
980 mergestatemod.ACTION_CREATED,
995 (fl2,),
981 (fl2,),
996 b'remote created',
982 b'remote created',
997 )
983 )
998 else:
984 else:
999 mresult.addfile(
985 mresult.addfile(
1000 f,
986 f,
1001 mergestatemod.ACTION_CREATED_MERGE,
987 mergestatemod.ACTION_CREATED_MERGE,
1002 (fl2, pa.node()),
988 (fl2, pa.node()),
1003 b'remote created, get or merge',
989 b'remote created, get or merge',
1004 )
990 )
1005 elif n2 != ma[f]:
991 elif n2 != ma[f]:
1006 df = None
992 df = None
1007 for d in branch_copies1.dirmove:
993 for d in branch_copies1.dirmove:
1008 if f.startswith(d):
994 if f.startswith(d):
1009 # new file added in a directory that was moved
995 # new file added in a directory that was moved
1010 df = branch_copies1.dirmove[d] + f[len(d) :]
996 df = branch_copies1.dirmove[d] + f[len(d) :]
1011 break
997 break
1012 if df is not None and df in m1:
998 if df is not None and df in m1:
1013 mresult.addfile(
999 mresult.addfile(
1014 df,
1000 df,
1015 mergestatemod.ACTION_MERGE,
1001 mergestatemod.ACTION_MERGE,
1016 (df, f, f, False, pa.node()),
1002 (df, f, f, False, pa.node()),
1017 b'local directory rename - respect move '
1003 b'local directory rename - respect move '
1018 b'from %s' % f,
1004 b'from %s' % f,
1019 )
1005 )
1020 elif acceptremote:
1006 elif acceptremote:
1021 mresult.addfile(
1007 mresult.addfile(
1022 f,
1008 f,
1023 mergestatemod.ACTION_CREATED,
1009 mergestatemod.ACTION_CREATED,
1024 (fl2,),
1010 (fl2,),
1025 b'remote recreating',
1011 b'remote recreating',
1026 )
1012 )
1027 else:
1013 else:
1028 mresult.addfile(
1014 mresult.addfile(
1029 f,
1015 f,
1030 mergestatemod.ACTION_DELETED_CHANGED,
1016 mergestatemod.ACTION_DELETED_CHANGED,
1031 (None, f, f, False, pa.node()),
1017 (None, f, f, False, pa.node()),
1032 b'prompt deleted/changed',
1018 b'prompt deleted/changed',
1033 )
1019 )
1034 else:
1020 else:
1035 mresult.addfile(
1021 mresult.addfile(
1036 f,
1022 f,
1037 mergestatemod.ACTION_KEEP_ABSENT,
1023 mergestatemod.ACTION_KEEP_ABSENT,
1038 None,
1024 None,
1039 b'local not present, remote unchanged',
1025 b'local not present, remote unchanged',
1040 )
1026 )
1041
1027
1042 if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'):
1028 if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'):
1043 # If we are merging, look for path conflicts.
1029 # If we are merging, look for path conflicts.
1044 checkpathconflicts(repo, wctx, p2, mresult)
1030 checkpathconflicts(repo, wctx, p2, mresult)
1045
1031
1046 narrowmatch = repo.narrowmatch()
1032 narrowmatch = repo.narrowmatch()
1047 if not narrowmatch.always():
1033 if not narrowmatch.always():
1048 # Updates "actions" in place
1034 # Updates "actions" in place
1049 _filternarrowactions(narrowmatch, branchmerge, mresult)
1035 _filternarrowactions(narrowmatch, branchmerge, mresult)
1050
1036
1051 renamedelete = branch_copies1.renamedelete
1037 renamedelete = branch_copies1.renamedelete
1052 renamedelete.update(branch_copies2.renamedelete)
1038 renamedelete.update(branch_copies2.renamedelete)
1053
1039
1054 mresult.updatevalues(diverge, renamedelete)
1040 mresult.updatevalues(diverge, renamedelete)
1055 return mresult
1041 return mresult
1056
1042
1057
1043
1058 def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
1044 def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
1059 """Resolves false conflicts where the nodeid changed but the content
1045 """Resolves false conflicts where the nodeid changed but the content
1060 remained the same."""
1046 remained the same."""
1061 # We force a copy of actions.items() because we're going to mutate
1047 # We force a copy of actions.items() because we're going to mutate
1062 # actions as we resolve trivial conflicts.
1048 # actions as we resolve trivial conflicts.
1063 for f in list(mresult.files((mergestatemod.ACTION_CHANGED_DELETED,))):
1049 for f in list(mresult.files((mergestatemod.ACTION_CHANGED_DELETED,))):
1064 if f in ancestor and not wctx[f].cmp(ancestor[f]):
1050 if f in ancestor and not wctx[f].cmp(ancestor[f]):
1065 # local did change but ended up with same content
1051 # local did change but ended up with same content
1066 mresult.addfile(
1052 mresult.addfile(
1067 f, mergestatemod.ACTION_REMOVE, None, b'prompt same'
1053 f, mergestatemod.ACTION_REMOVE, None, b'prompt same'
1068 )
1054 )
1069
1055
1070 for f in list(mresult.files((mergestatemod.ACTION_DELETED_CHANGED,))):
1056 for f in list(mresult.files((mergestatemod.ACTION_DELETED_CHANGED,))):
1071 if f in ancestor and not mctx[f].cmp(ancestor[f]):
1057 if f in ancestor and not mctx[f].cmp(ancestor[f]):
1072 # remote did change but ended up with same content
1058 # remote did change but ended up with same content
1073 mresult.removefile(f) # don't get = keep local deleted
1059 mresult.removefile(f) # don't get = keep local deleted
1074
1060
1075
1061
1076 def calculateupdates(
1062 def calculateupdates(
1077 repo,
1063 repo,
1078 wctx,
1064 wctx,
1079 mctx,
1065 mctx,
1080 ancestors,
1066 ancestors,
1081 branchmerge,
1067 branchmerge,
1082 force,
1068 force,
1083 acceptremote,
1069 acceptremote,
1084 followcopies,
1070 followcopies,
1085 matcher=None,
1071 matcher=None,
1086 mergeforce=False,
1072 mergeforce=False,
1087 ):
1073 ):
1088 """
1074 """
1089 Calculate the actions needed to merge mctx into wctx using ancestors
1075 Calculate the actions needed to merge mctx into wctx using ancestors
1090
1076
1091 Uses manifestmerge() to merge manifest and get list of actions required to
1077 Uses manifestmerge() to merge manifest and get list of actions required to
1092 perform for merging two manifests. If there are multiple ancestors, uses bid
1078 perform for merging two manifests. If there are multiple ancestors, uses bid
1093 merge if enabled.
1079 merge if enabled.
1094
1080
1095 Also filters out actions which are unrequired if repository is sparse.
1081 Also filters out actions which are unrequired if repository is sparse.
1096
1082
1097 Returns mergeresult object same as manifestmerge().
1083 Returns mergeresult object same as manifestmerge().
1098 """
1084 """
1099 # Avoid cycle.
1085 # Avoid cycle.
1100 from . import sparse
1086 from . import sparse
1101
1087
1102 mresult = None
1088 mresult = None
1103 if len(ancestors) == 1: # default
1089 if len(ancestors) == 1: # default
1104 mresult = manifestmerge(
1090 mresult = manifestmerge(
1105 repo,
1091 repo,
1106 wctx,
1092 wctx,
1107 mctx,
1093 mctx,
1108 ancestors[0],
1094 ancestors[0],
1109 branchmerge,
1095 branchmerge,
1110 force,
1096 force,
1111 matcher,
1097 matcher,
1112 acceptremote,
1098 acceptremote,
1113 followcopies,
1099 followcopies,
1114 )
1100 )
1115 _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce)
1101 _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce)
1116
1102
1117 else: # only when merge.preferancestor=* - the default
1103 else: # only when merge.preferancestor=* - the default
1118 repo.ui.note(
1104 repo.ui.note(
1119 _(b"note: merging %s and %s using bids from ancestors %s\n")
1105 _(b"note: merging %s and %s using bids from ancestors %s\n")
1120 % (
1106 % (
1121 wctx,
1107 wctx,
1122 mctx,
1108 mctx,
1123 _(b' and ').join(pycompat.bytestr(anc) for anc in ancestors),
1109 _(b' and ').join(pycompat.bytestr(anc) for anc in ancestors),
1124 )
1110 )
1125 )
1111 )
1126
1112
1127 # mapping filename to bids (action method to list af actions)
1113 # mapping filename to bids (action method to list af actions)
1128 # {FILENAME1 : BID1, FILENAME2 : BID2}
1114 # {FILENAME1 : BID1, FILENAME2 : BID2}
1129 # BID is another dictionary which contains
1115 # BID is another dictionary which contains
1130 # mapping of following form:
1116 # mapping of following form:
1131 # {ACTION_X : [info, ..], ACTION_Y : [info, ..]}
1117 # {ACTION_X : [info, ..], ACTION_Y : [info, ..]}
1132 fbids = {}
1118 fbids = {}
1133 mresult = mergeresult()
1119 mresult = mergeresult()
1134 diverge, renamedelete = None, None
1120 diverge, renamedelete = None, None
1135 for ancestor in ancestors:
1121 for ancestor in ancestors:
1136 repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor)
1122 repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor)
1137 mresult1 = manifestmerge(
1123 mresult1 = manifestmerge(
1138 repo,
1124 repo,
1139 wctx,
1125 wctx,
1140 mctx,
1126 mctx,
1141 ancestor,
1127 ancestor,
1142 branchmerge,
1128 branchmerge,
1143 force,
1129 force,
1144 matcher,
1130 matcher,
1145 acceptremote,
1131 acceptremote,
1146 followcopies,
1132 followcopies,
1147 forcefulldiff=True,
1133 forcefulldiff=True,
1148 )
1134 )
1149 _checkunknownfiles(repo, wctx, mctx, force, mresult1, mergeforce)
1135 _checkunknownfiles(repo, wctx, mctx, force, mresult1, mergeforce)
1150
1136
1151 # Track the shortest set of warning on the theory that bid
1137 # Track the shortest set of warning on the theory that bid
1152 # merge will correctly incorporate more information
1138 # merge will correctly incorporate more information
1153 if diverge is None or len(mresult1.diverge) < len(diverge):
1139 if diverge is None or len(mresult1.diverge) < len(diverge):
1154 diverge = mresult1.diverge
1140 diverge = mresult1.diverge
1155 if renamedelete is None or len(renamedelete) < len(
1141 if renamedelete is None or len(renamedelete) < len(
1156 mresult1.renamedelete
1142 mresult1.renamedelete
1157 ):
1143 ):
1158 renamedelete = mresult1.renamedelete
1144 renamedelete = mresult1.renamedelete
1159
1145
1160 # blindly update final mergeresult commitinfo with what we get
1146 # blindly update final mergeresult commitinfo with what we get
1161 # from mergeresult object for each ancestor
1147 # from mergeresult object for each ancestor
1162 # TODO: some commitinfo depends on what bid merge choose and hence
1148 # TODO: some commitinfo depends on what bid merge choose and hence
1163 # we will need to make commitinfo also depend on bid merge logic
1149 # we will need to make commitinfo also depend on bid merge logic
1164 mresult._commitinfo.update(mresult1._commitinfo)
1150 mresult._commitinfo.update(mresult1._commitinfo)
1165
1151
1166 for f, a in mresult1.filemap(sort=True):
1152 for f, a in mresult1.filemap(sort=True):
1167 m, args, msg = a
1153 m, args, msg = a
1168 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m))
1154 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m))
1169 if f in fbids:
1155 if f in fbids:
1170 d = fbids[f]
1156 d = fbids[f]
1171 if m in d:
1157 if m in d:
1172 d[m].append(a)
1158 d[m].append(a)
1173 else:
1159 else:
1174 d[m] = [a]
1160 d[m] = [a]
1175 else:
1161 else:
1176 fbids[f] = {m: [a]}
1162 fbids[f] = {m: [a]}
1177
1163
1178 # Call for bids
1164 # Call for bids
1179 # Pick the best bid for each file
1165 # Pick the best bid for each file
1180 repo.ui.note(
1166 repo.ui.note(
1181 _(b'\nauction for merging merge bids (%d ancestors)\n')
1167 _(b'\nauction for merging merge bids (%d ancestors)\n')
1182 % len(ancestors)
1168 % len(ancestors)
1183 )
1169 )
1184 for f, bids in sorted(fbids.items()):
1170 for f, bids in sorted(fbids.items()):
1185 if repo.ui.debugflag:
1171 if repo.ui.debugflag:
1186 repo.ui.debug(b" list of bids for %s:\n" % f)
1172 repo.ui.debug(b" list of bids for %s:\n" % f)
1187 for m, l in sorted(bids.items()):
1173 for m, l in sorted(bids.items()):
1188 for _f, args, msg in l:
1174 for _f, args, msg in l:
1189 repo.ui.debug(b' %s -> %s\n' % (msg, m))
1175 repo.ui.debug(b' %s -> %s\n' % (msg, m))
1190 # bids is a mapping from action method to list af actions
1176 # bids is a mapping from action method to list af actions
1191 # Consensus?
1177 # Consensus?
1192 if len(bids) == 1: # all bids are the same kind of method
1178 if len(bids) == 1: # all bids are the same kind of method
1193 m, l = list(bids.items())[0]
1179 m, l = list(bids.items())[0]
1194 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1180 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1195 repo.ui.note(_(b" %s: consensus for %s\n") % (f, m))
1181 repo.ui.note(_(b" %s: consensus for %s\n") % (f, m))
1196 mresult.addfile(f, *l[0])
1182 mresult.addfile(f, *l[0])
1197 continue
1183 continue
1198 # If keep is an option, just do it.
1184 # If keep is an option, just do it.
1199 if mergestatemod.ACTION_KEEP in bids:
1185 if mergestatemod.ACTION_KEEP in bids:
1200 repo.ui.note(_(b" %s: picking 'keep' action\n") % f)
1186 repo.ui.note(_(b" %s: picking 'keep' action\n") % f)
1201 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0])
1187 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0])
1202 continue
1188 continue
1203 # If keep absent is an option, just do that
1189 # If keep absent is an option, just do that
1204 if mergestatemod.ACTION_KEEP_ABSENT in bids:
1190 if mergestatemod.ACTION_KEEP_ABSENT in bids:
1205 repo.ui.note(_(b" %s: picking 'keep absent' action\n") % f)
1191 repo.ui.note(_(b" %s: picking 'keep absent' action\n") % f)
1206 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP_ABSENT][0])
1192 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP_ABSENT][0])
1207 continue
1193 continue
1208 # If there are gets and they all agree [how could they not?], do it.
1194 # If there are gets and they all agree [how could they not?], do it.
1209 if mergestatemod.ACTION_GET in bids:
1195 if mergestatemod.ACTION_GET in bids:
1210 ga0 = bids[mergestatemod.ACTION_GET][0]
1196 ga0 = bids[mergestatemod.ACTION_GET][0]
1211 if all(a == ga0 for a in bids[mergestatemod.ACTION_GET][1:]):
1197 if all(a == ga0 for a in bids[mergestatemod.ACTION_GET][1:]):
1212 repo.ui.note(_(b" %s: picking 'get' action\n") % f)
1198 repo.ui.note(_(b" %s: picking 'get' action\n") % f)
1213 mresult.addfile(f, *ga0)
1199 mresult.addfile(f, *ga0)
1214 continue
1200 continue
1215 # TODO: Consider other simple actions such as mode changes
1201 # TODO: Consider other simple actions such as mode changes
1216 # Handle inefficient democrazy.
1202 # Handle inefficient democrazy.
1217 repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f)
1203 repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f)
1218 for m, l in sorted(bids.items()):
1204 for m, l in sorted(bids.items()):
1219 for _f, args, msg in l:
1205 for _f, args, msg in l:
1220 repo.ui.note(b' %s -> %s\n' % (msg, m))
1206 repo.ui.note(b' %s -> %s\n' % (msg, m))
1221 # Pick random action. TODO: Instead, prompt user when resolving
1207 # Pick random action. TODO: Instead, prompt user when resolving
1222 m, l = list(bids.items())[0]
1208 m, l = list(bids.items())[0]
1223 repo.ui.warn(
1209 repo.ui.warn(
1224 _(b' %s: ambiguous merge - picked %s action\n') % (f, m)
1210 _(b' %s: ambiguous merge - picked %s action\n') % (f, m)
1225 )
1211 )
1226 mresult.addfile(f, *l[0])
1212 mresult.addfile(f, *l[0])
1227 continue
1213 continue
1228 repo.ui.note(_(b'end of auction\n\n'))
1214 repo.ui.note(_(b'end of auction\n\n'))
1229 mresult.updatevalues(diverge, renamedelete)
1215 mresult.updatevalues(diverge, renamedelete)
1230
1216
1231 if wctx.rev() is None:
1217 if wctx.rev() is None:
1232 _forgetremoved(wctx, mctx, branchmerge, mresult)
1218 _forgetremoved(wctx, mctx, branchmerge, mresult)
1233
1219
1234 sparse.filterupdatesactions(repo, wctx, mctx, branchmerge, mresult)
1220 sparse.filterupdatesactions(repo, wctx, mctx, branchmerge, mresult)
1235 _resolvetrivial(repo, wctx, mctx, ancestors[0], mresult)
1221 _resolvetrivial(repo, wctx, mctx, ancestors[0], mresult)
1236
1222
1237 return mresult
1223 return mresult
1238
1224
1239
1225
1240 def _getcwd():
1226 def _getcwd():
1241 try:
1227 try:
1242 return encoding.getcwd()
1228 return encoding.getcwd()
1243 except OSError as err:
1229 except OSError as err:
1244 if err.errno == errno.ENOENT:
1230 if err.errno == errno.ENOENT:
1245 return None
1231 return None
1246 raise
1232 raise
1247
1233
1248
1234
1249 def batchremove(repo, wctx, actions):
1235 def batchremove(repo, wctx, actions):
1250 """apply removes to the working directory
1236 """apply removes to the working directory
1251
1237
1252 yields tuples for progress updates
1238 yields tuples for progress updates
1253 """
1239 """
1254 verbose = repo.ui.verbose
1240 verbose = repo.ui.verbose
1255 cwd = _getcwd()
1241 cwd = _getcwd()
1256 i = 0
1242 i = 0
1257 for f, args, msg in actions:
1243 for f, args, msg in actions:
1258 repo.ui.debug(b" %s: %s -> r\n" % (f, msg))
1244 repo.ui.debug(b" %s: %s -> r\n" % (f, msg))
1259 if verbose:
1245 if verbose:
1260 repo.ui.note(_(b"removing %s\n") % f)
1246 repo.ui.note(_(b"removing %s\n") % f)
1261 wctx[f].audit()
1247 wctx[f].audit()
1262 try:
1248 try:
1263 wctx[f].remove(ignoremissing=True)
1249 wctx[f].remove(ignoremissing=True)
1264 except OSError as inst:
1250 except OSError as inst:
1265 repo.ui.warn(
1251 repo.ui.warn(
1266 _(b"update failed to remove %s: %s!\n") % (f, inst.strerror)
1252 _(b"update failed to remove %s: %s!\n") % (f, inst.strerror)
1267 )
1253 )
1268 if i == 100:
1254 if i == 100:
1269 yield i, f
1255 yield i, f
1270 i = 0
1256 i = 0
1271 i += 1
1257 i += 1
1272 if i > 0:
1258 if i > 0:
1273 yield i, f
1259 yield i, f
1274
1260
1275 if cwd and not _getcwd():
1261 if cwd and not _getcwd():
1276 # cwd was removed in the course of removing files; print a helpful
1262 # cwd was removed in the course of removing files; print a helpful
1277 # warning.
1263 # warning.
1278 repo.ui.warn(
1264 repo.ui.warn(
1279 _(
1265 _(
1280 b"current directory was removed\n"
1266 b"current directory was removed\n"
1281 b"(consider changing to repo root: %s)\n"
1267 b"(consider changing to repo root: %s)\n"
1282 )
1268 )
1283 % repo.root
1269 % repo.root
1284 )
1270 )
1285
1271
1286
1272
1287 def batchget(repo, mctx, wctx, wantfiledata, actions):
1273 def batchget(repo, mctx, wctx, wantfiledata, actions):
1288 """apply gets to the working directory
1274 """apply gets to the working directory
1289
1275
1290 mctx is the context to get from
1276 mctx is the context to get from
1291
1277
1292 Yields arbitrarily many (False, tuple) for progress updates, followed by
1278 Yields arbitrarily many (False, tuple) for progress updates, followed by
1293 exactly one (True, filedata). When wantfiledata is false, filedata is an
1279 exactly one (True, filedata). When wantfiledata is false, filedata is an
1294 empty dict. When wantfiledata is true, filedata[f] is a triple (mode, size,
1280 empty dict. When wantfiledata is true, filedata[f] is a triple (mode, size,
1295 mtime) of the file f written for each action.
1281 mtime) of the file f written for each action.
1296 """
1282 """
1297 filedata = {}
1283 filedata = {}
1298 verbose = repo.ui.verbose
1284 verbose = repo.ui.verbose
1299 fctx = mctx.filectx
1285 fctx = mctx.filectx
1300 ui = repo.ui
1286 ui = repo.ui
1301 i = 0
1287 i = 0
1302 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1288 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1303 for f, (flags, backup), msg in actions:
1289 for f, (flags, backup), msg in actions:
1304 repo.ui.debug(b" %s: %s -> g\n" % (f, msg))
1290 repo.ui.debug(b" %s: %s -> g\n" % (f, msg))
1305 if verbose:
1291 if verbose:
1306 repo.ui.note(_(b"getting %s\n") % f)
1292 repo.ui.note(_(b"getting %s\n") % f)
1307
1293
1308 if backup:
1294 if backup:
1309 # If a file or directory exists with the same name, back that
1295 # If a file or directory exists with the same name, back that
1310 # up. Otherwise, look to see if there is a file that conflicts
1296 # up. Otherwise, look to see if there is a file that conflicts
1311 # with a directory this file is in, and if so, back that up.
1297 # with a directory this file is in, and if so, back that up.
1312 conflicting = f
1298 conflicting = f
1313 if not repo.wvfs.lexists(f):
1299 if not repo.wvfs.lexists(f):
1314 for p in pathutil.finddirs(f):
1300 for p in pathutil.finddirs(f):
1315 if repo.wvfs.isfileorlink(p):
1301 if repo.wvfs.isfileorlink(p):
1316 conflicting = p
1302 conflicting = p
1317 break
1303 break
1318 if repo.wvfs.lexists(conflicting):
1304 if repo.wvfs.lexists(conflicting):
1319 orig = scmutil.backuppath(ui, repo, conflicting)
1305 orig = scmutil.backuppath(ui, repo, conflicting)
1320 util.rename(repo.wjoin(conflicting), orig)
1306 util.rename(repo.wjoin(conflicting), orig)
1321 wfctx = wctx[f]
1307 wfctx = wctx[f]
1322 wfctx.clearunknown()
1308 wfctx.clearunknown()
1323 atomictemp = ui.configbool(b"experimental", b"update.atomic-file")
1309 atomictemp = ui.configbool(b"experimental", b"update.atomic-file")
1324 size = wfctx.write(
1310 size = wfctx.write(
1325 fctx(f).data(),
1311 fctx(f).data(),
1326 flags,
1312 flags,
1327 backgroundclose=True,
1313 backgroundclose=True,
1328 atomictemp=atomictemp,
1314 atomictemp=atomictemp,
1329 )
1315 )
1330 if wantfiledata:
1316 if wantfiledata:
1331 s = wfctx.lstat()
1317 s = wfctx.lstat()
1332 mode = s.st_mode
1318 mode = s.st_mode
1333 mtime = s[stat.ST_MTIME]
1319 mtime = s[stat.ST_MTIME]
1334 filedata[f] = (mode, size, mtime) # for dirstate.normal
1320 filedata[f] = (mode, size, mtime) # for dirstate.normal
1335 if i == 100:
1321 if i == 100:
1336 yield False, (i, f)
1322 yield False, (i, f)
1337 i = 0
1323 i = 0
1338 i += 1
1324 i += 1
1339 if i > 0:
1325 if i > 0:
1340 yield False, (i, f)
1326 yield False, (i, f)
1341 yield True, filedata
1327 yield True, filedata
1342
1328
1343
1329
1344 def _prefetchfiles(repo, ctx, mresult):
1330 def _prefetchfiles(repo, ctx, mresult):
1345 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1331 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1346 of merge actions. ``ctx`` is the context being merged in."""
1332 of merge actions. ``ctx`` is the context being merged in."""
1347
1333
1348 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1334 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1349 # don't touch the context to be merged in. 'cd' is skipped, because
1335 # don't touch the context to be merged in. 'cd' is skipped, because
1350 # changed/deleted never resolves to something from the remote side.
1336 # changed/deleted never resolves to something from the remote side.
1351 files = mresult.files(
1337 files = mresult.files(
1352 [
1338 [
1353 mergestatemod.ACTION_GET,
1339 mergestatemod.ACTION_GET,
1354 mergestatemod.ACTION_DELETED_CHANGED,
1340 mergestatemod.ACTION_DELETED_CHANGED,
1355 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1341 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1356 mergestatemod.ACTION_MERGE,
1342 mergestatemod.ACTION_MERGE,
1357 ]
1343 ]
1358 )
1344 )
1359
1345
1360 prefetch = scmutil.prefetchfiles
1346 prefetch = scmutil.prefetchfiles
1361 matchfiles = scmutil.matchfiles
1347 matchfiles = scmutil.matchfiles
1362 prefetch(
1348 prefetch(
1363 repo, [(ctx.rev(), matchfiles(repo, files),)],
1349 repo, [(ctx.rev(), matchfiles(repo, files),)],
1364 )
1350 )
1365
1351
1366
1352
1367 @attr.s(frozen=True)
1353 @attr.s(frozen=True)
1368 class updateresult(object):
1354 class updateresult(object):
1369 updatedcount = attr.ib()
1355 updatedcount = attr.ib()
1370 mergedcount = attr.ib()
1356 mergedcount = attr.ib()
1371 removedcount = attr.ib()
1357 removedcount = attr.ib()
1372 unresolvedcount = attr.ib()
1358 unresolvedcount = attr.ib()
1373
1359
1374 def isempty(self):
1360 def isempty(self):
1375 return not (
1361 return not (
1376 self.updatedcount
1362 self.updatedcount
1377 or self.mergedcount
1363 or self.mergedcount
1378 or self.removedcount
1364 or self.removedcount
1379 or self.unresolvedcount
1365 or self.unresolvedcount
1380 )
1366 )
1381
1367
1382
1368
1383 def applyupdates(
1369 def applyupdates(
1384 repo, mresult, wctx, mctx, overwrite, wantfiledata, labels=None,
1370 repo, mresult, wctx, mctx, overwrite, wantfiledata, labels=None,
1385 ):
1371 ):
1386 """apply the merge action list to the working directory
1372 """apply the merge action list to the working directory
1387
1373
1388 mresult is a mergeresult object representing result of the merge
1374 mresult is a mergeresult object representing result of the merge
1389 wctx is the working copy context
1375 wctx is the working copy context
1390 mctx is the context to be merged into the working copy
1376 mctx is the context to be merged into the working copy
1391
1377
1392 Return a tuple of (counts, filedata), where counts is a tuple
1378 Return a tuple of (counts, filedata), where counts is a tuple
1393 (updated, merged, removed, unresolved) that describes how many
1379 (updated, merged, removed, unresolved) that describes how many
1394 files were affected by the update, and filedata is as described in
1380 files were affected by the update, and filedata is as described in
1395 batchget.
1381 batchget.
1396 """
1382 """
1397
1383
1398 _prefetchfiles(repo, mctx, mresult)
1384 _prefetchfiles(repo, mctx, mresult)
1399
1385
1400 updated, merged, removed = 0, 0, 0
1386 updated, merged, removed = 0, 0, 0
1401 ms = wctx.mergestate(clean=True)
1387 ms = wctx.mergestate(clean=True)
1402 ms.start(wctx.p1().node(), mctx.node(), labels)
1388 ms.start(wctx.p1().node(), mctx.node(), labels)
1403
1389
1404 for f, op in pycompat.iteritems(mresult.commitinfo):
1390 for f, op in pycompat.iteritems(mresult.commitinfo):
1405 # the other side of filenode was choosen while merging, store this in
1391 # the other side of filenode was choosen while merging, store this in
1406 # mergestate so that it can be reused on commit
1392 # mergestate so that it can be reused on commit
1407 ms.addcommitinfo(f, op)
1393 ms.addcommitinfo(f, op)
1408
1394
1409 numupdates = mresult.len() - mresult.len(mergeresult.NO_OP_ACTIONS)
1395 numupdates = mresult.len() - mresult.len(mergeresult.NO_OP_ACTIONS)
1410 progress = repo.ui.makeprogress(
1396 progress = repo.ui.makeprogress(
1411 _(b'updating'), unit=_(b'files'), total=numupdates
1397 _(b'updating'), unit=_(b'files'), total=numupdates
1412 )
1398 )
1413
1399
1414 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_REMOVE]:
1400 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_REMOVE]:
1415 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1401 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1416
1402
1417 # record path conflicts
1403 # record path conflicts
1418 for f, args, msg in mresult.getactions(
1404 for f, args, msg in mresult.getactions(
1419 [mergestatemod.ACTION_PATH_CONFLICT], sort=True
1405 [mergestatemod.ACTION_PATH_CONFLICT], sort=True
1420 ):
1406 ):
1421 f1, fo = args
1407 f1, fo = args
1422 s = repo.ui.status
1408 s = repo.ui.status
1423 s(
1409 s(
1424 _(
1410 _(
1425 b"%s: path conflict - a file or link has the same name as a "
1411 b"%s: path conflict - a file or link has the same name as a "
1426 b"directory\n"
1412 b"directory\n"
1427 )
1413 )
1428 % f
1414 % f
1429 )
1415 )
1430 if fo == b'l':
1416 if fo == b'l':
1431 s(_(b"the local file has been renamed to %s\n") % f1)
1417 s(_(b"the local file has been renamed to %s\n") % f1)
1432 else:
1418 else:
1433 s(_(b"the remote file has been renamed to %s\n") % f1)
1419 s(_(b"the remote file has been renamed to %s\n") % f1)
1434 s(_(b"resolve manually then use 'hg resolve --mark %s'\n") % f)
1420 s(_(b"resolve manually then use 'hg resolve --mark %s'\n") % f)
1435 ms.addpathconflict(f, f1, fo)
1421 ms.addpathconflict(f, f1, fo)
1436 progress.increment(item=f)
1422 progress.increment(item=f)
1437
1423
1438 # When merging in-memory, we can't support worker processes, so set the
1424 # When merging in-memory, we can't support worker processes, so set the
1439 # per-item cost at 0 in that case.
1425 # per-item cost at 0 in that case.
1440 cost = 0 if wctx.isinmemory() else 0.001
1426 cost = 0 if wctx.isinmemory() else 0.001
1441
1427
1442 # remove in parallel (must come before resolving path conflicts and getting)
1428 # remove in parallel (must come before resolving path conflicts and getting)
1443 prog = worker.worker(
1429 prog = worker.worker(
1444 repo.ui,
1430 repo.ui,
1445 cost,
1431 cost,
1446 batchremove,
1432 batchremove,
1447 (repo, wctx),
1433 (repo, wctx),
1448 list(mresult.getactions([mergestatemod.ACTION_REMOVE], sort=True)),
1434 list(mresult.getactions([mergestatemod.ACTION_REMOVE], sort=True)),
1449 )
1435 )
1450 for i, item in prog:
1436 for i, item in prog:
1451 progress.increment(step=i, item=item)
1437 progress.increment(step=i, item=item)
1452 removed = mresult.len((mergestatemod.ACTION_REMOVE,))
1438 removed = mresult.len((mergestatemod.ACTION_REMOVE,))
1453
1439
1454 # resolve path conflicts (must come before getting)
1440 # resolve path conflicts (must come before getting)
1455 for f, args, msg in mresult.getactions(
1441 for f, args, msg in mresult.getactions(
1456 [mergestatemod.ACTION_PATH_CONFLICT_RESOLVE], sort=True
1442 [mergestatemod.ACTION_PATH_CONFLICT_RESOLVE], sort=True
1457 ):
1443 ):
1458 repo.ui.debug(b" %s: %s -> pr\n" % (f, msg))
1444 repo.ui.debug(b" %s: %s -> pr\n" % (f, msg))
1459 (f0, origf0) = args
1445 (f0, origf0) = args
1460 if wctx[f0].lexists():
1446 if wctx[f0].lexists():
1461 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1447 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1462 wctx[f].audit()
1448 wctx[f].audit()
1463 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1449 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1464 wctx[f0].remove()
1450 wctx[f0].remove()
1465 progress.increment(item=f)
1451 progress.increment(item=f)
1466
1452
1467 # get in parallel.
1453 # get in parallel.
1468 threadsafe = repo.ui.configbool(
1454 threadsafe = repo.ui.configbool(
1469 b'experimental', b'worker.wdir-get-thread-safe'
1455 b'experimental', b'worker.wdir-get-thread-safe'
1470 )
1456 )
1471 prog = worker.worker(
1457 prog = worker.worker(
1472 repo.ui,
1458 repo.ui,
1473 cost,
1459 cost,
1474 batchget,
1460 batchget,
1475 (repo, mctx, wctx, wantfiledata),
1461 (repo, mctx, wctx, wantfiledata),
1476 list(mresult.getactions([mergestatemod.ACTION_GET], sort=True)),
1462 list(mresult.getactions([mergestatemod.ACTION_GET], sort=True)),
1477 threadsafe=threadsafe,
1463 threadsafe=threadsafe,
1478 hasretval=True,
1464 hasretval=True,
1479 )
1465 )
1480 getfiledata = {}
1466 getfiledata = {}
1481 for final, res in prog:
1467 for final, res in prog:
1482 if final:
1468 if final:
1483 getfiledata = res
1469 getfiledata = res
1484 else:
1470 else:
1485 i, item = res
1471 i, item = res
1486 progress.increment(step=i, item=item)
1472 progress.increment(step=i, item=item)
1487
1473
1488 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_GET]:
1474 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_GET]:
1489 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1475 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1490
1476
1491 # forget (manifest only, just log it) (must come first)
1477 # forget (manifest only, just log it) (must come first)
1492 for f, args, msg in mresult.getactions(
1478 for f, args, msg in mresult.getactions(
1493 (mergestatemod.ACTION_FORGET,), sort=True
1479 (mergestatemod.ACTION_FORGET,), sort=True
1494 ):
1480 ):
1495 repo.ui.debug(b" %s: %s -> f\n" % (f, msg))
1481 repo.ui.debug(b" %s: %s -> f\n" % (f, msg))
1496 progress.increment(item=f)
1482 progress.increment(item=f)
1497
1483
1498 # re-add (manifest only, just log it)
1484 # re-add (manifest only, just log it)
1499 for f, args, msg in mresult.getactions(
1485 for f, args, msg in mresult.getactions(
1500 (mergestatemod.ACTION_ADD,), sort=True
1486 (mergestatemod.ACTION_ADD,), sort=True
1501 ):
1487 ):
1502 repo.ui.debug(b" %s: %s -> a\n" % (f, msg))
1488 repo.ui.debug(b" %s: %s -> a\n" % (f, msg))
1503 progress.increment(item=f)
1489 progress.increment(item=f)
1504
1490
1505 # re-add/mark as modified (manifest only, just log it)
1491 # re-add/mark as modified (manifest only, just log it)
1506 for f, args, msg in mresult.getactions(
1492 for f, args, msg in mresult.getactions(
1507 (mergestatemod.ACTION_ADD_MODIFIED,), sort=True
1493 (mergestatemod.ACTION_ADD_MODIFIED,), sort=True
1508 ):
1494 ):
1509 repo.ui.debug(b" %s: %s -> am\n" % (f, msg))
1495 repo.ui.debug(b" %s: %s -> am\n" % (f, msg))
1510 progress.increment(item=f)
1496 progress.increment(item=f)
1511
1497
1512 # keep (noop, just log it)
1498 # keep (noop, just log it)
1513 for f, args, msg in mresult.getactions(
1499 for f, args, msg in mresult.getactions(
1514 (mergestatemod.ACTION_KEEP,), sort=True
1500 (mergestatemod.ACTION_KEEP,), sort=True
1515 ):
1501 ):
1516 repo.ui.debug(b" %s: %s -> k\n" % (f, msg))
1502 repo.ui.debug(b" %s: %s -> k\n" % (f, msg))
1517 # no progress
1503 # no progress
1518 for f, args, msg in mresult.getactions(
1504 for f, args, msg in mresult.getactions(
1519 (mergestatemod.ACTION_KEEP_ABSENT,), sort=True
1505 (mergestatemod.ACTION_KEEP_ABSENT,), sort=True
1520 ):
1506 ):
1521 repo.ui.debug(b" %s: %s -> ka\n" % (f, msg))
1507 repo.ui.debug(b" %s: %s -> ka\n" % (f, msg))
1522 # no progress
1508 # no progress
1523
1509
1524 # directory rename, move local
1510 # directory rename, move local
1525 for f, args, msg in mresult.getactions(
1511 for f, args, msg in mresult.getactions(
1526 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,), sort=True
1512 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,), sort=True
1527 ):
1513 ):
1528 repo.ui.debug(b" %s: %s -> dm\n" % (f, msg))
1514 repo.ui.debug(b" %s: %s -> dm\n" % (f, msg))
1529 progress.increment(item=f)
1515 progress.increment(item=f)
1530 f0, flags = args
1516 f0, flags = args
1531 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1517 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1532 wctx[f].audit()
1518 wctx[f].audit()
1533 wctx[f].write(wctx.filectx(f0).data(), flags)
1519 wctx[f].write(wctx.filectx(f0).data(), flags)
1534 wctx[f0].remove()
1520 wctx[f0].remove()
1535
1521
1536 # local directory rename, get
1522 # local directory rename, get
1537 for f, args, msg in mresult.getactions(
1523 for f, args, msg in mresult.getactions(
1538 (mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,), sort=True
1524 (mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,), sort=True
1539 ):
1525 ):
1540 repo.ui.debug(b" %s: %s -> dg\n" % (f, msg))
1526 repo.ui.debug(b" %s: %s -> dg\n" % (f, msg))
1541 progress.increment(item=f)
1527 progress.increment(item=f)
1542 f0, flags = args
1528 f0, flags = args
1543 repo.ui.note(_(b"getting %s to %s\n") % (f0, f))
1529 repo.ui.note(_(b"getting %s to %s\n") % (f0, f))
1544 wctx[f].write(mctx.filectx(f0).data(), flags)
1530 wctx[f].write(mctx.filectx(f0).data(), flags)
1545
1531
1546 # exec
1532 # exec
1547 for f, args, msg in mresult.getactions(
1533 for f, args, msg in mresult.getactions(
1548 (mergestatemod.ACTION_EXEC,), sort=True
1534 (mergestatemod.ACTION_EXEC,), sort=True
1549 ):
1535 ):
1550 repo.ui.debug(b" %s: %s -> e\n" % (f, msg))
1536 repo.ui.debug(b" %s: %s -> e\n" % (f, msg))
1551 progress.increment(item=f)
1537 progress.increment(item=f)
1552 (flags,) = args
1538 (flags,) = args
1553 wctx[f].audit()
1539 wctx[f].audit()
1554 wctx[f].setflags(b'l' in flags, b'x' in flags)
1540 wctx[f].setflags(b'l' in flags, b'x' in flags)
1555
1541
1556 moves = []
1542 moves = []
1557
1543
1558 # 'cd' and 'dc' actions are treated like other merge conflicts
1544 # 'cd' and 'dc' actions are treated like other merge conflicts
1559 mergeactions = list(
1545 mergeactions = list(
1560 mresult.getactions(
1546 mresult.getactions(
1561 [
1547 [
1562 mergestatemod.ACTION_CHANGED_DELETED,
1548 mergestatemod.ACTION_CHANGED_DELETED,
1563 mergestatemod.ACTION_DELETED_CHANGED,
1549 mergestatemod.ACTION_DELETED_CHANGED,
1564 mergestatemod.ACTION_MERGE,
1550 mergestatemod.ACTION_MERGE,
1565 ],
1551 ],
1566 sort=True,
1552 sort=True,
1567 )
1553 )
1568 )
1554 )
1569 for f, args, msg in mergeactions:
1555 for f, args, msg in mergeactions:
1570 f1, f2, fa, move, anc = args
1556 f1, f2, fa, move, anc = args
1571 if f == b'.hgsubstate': # merged internally
1557 if f == b'.hgsubstate': # merged internally
1572 continue
1558 continue
1573 if f1 is None:
1559 if f1 is None:
1574 fcl = filemerge.absentfilectx(wctx, fa)
1560 fcl = filemerge.absentfilectx(wctx, fa)
1575 else:
1561 else:
1576 repo.ui.debug(b" preserving %s for resolve of %s\n" % (f1, f))
1562 repo.ui.debug(b" preserving %s for resolve of %s\n" % (f1, f))
1577 fcl = wctx[f1]
1563 fcl = wctx[f1]
1578 if f2 is None:
1564 if f2 is None:
1579 fco = filemerge.absentfilectx(mctx, fa)
1565 fco = filemerge.absentfilectx(mctx, fa)
1580 else:
1566 else:
1581 fco = mctx[f2]
1567 fco = mctx[f2]
1582 actx = repo[anc]
1568 actx = repo[anc]
1583 if fa in actx:
1569 if fa in actx:
1584 fca = actx[fa]
1570 fca = actx[fa]
1585 else:
1571 else:
1586 # TODO: move to absentfilectx
1572 # TODO: move to absentfilectx
1587 fca = repo.filectx(f1, fileid=nullrev)
1573 fca = repo.filectx(f1, fileid=nullrev)
1588 ms.add(fcl, fco, fca, f)
1574 ms.add(fcl, fco, fca, f)
1589 if f1 != f and move:
1575 if f1 != f and move:
1590 moves.append(f1)
1576 moves.append(f1)
1591
1577
1592 # remove renamed files after safely stored
1578 # remove renamed files after safely stored
1593 for f in moves:
1579 for f in moves:
1594 if wctx[f].lexists():
1580 if wctx[f].lexists():
1595 repo.ui.debug(b"removing %s\n" % f)
1581 repo.ui.debug(b"removing %s\n" % f)
1596 wctx[f].audit()
1582 wctx[f].audit()
1597 wctx[f].remove()
1583 wctx[f].remove()
1598
1584
1599 # these actions updates the file
1585 # these actions updates the file
1600 updated = mresult.len(
1586 updated = mresult.len(
1601 (
1587 (
1602 mergestatemod.ACTION_GET,
1588 mergestatemod.ACTION_GET,
1603 mergestatemod.ACTION_EXEC,
1589 mergestatemod.ACTION_EXEC,
1604 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1590 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1605 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
1591 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
1606 )
1592 )
1607 )
1593 )
1608 # the ordering is important here -- ms.mergedriver will raise if the merge
1609 # driver has changed, and we want to be able to bypass it when overwrite is
1610 # True
1611 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1612
1613 if usemergedriver:
1614 ms.commit()
1615 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1616 # the driver might leave some files unresolved
1617 unresolvedf = set(ms.unresolved())
1618 if not proceed:
1619 # XXX setting unresolved to at least 1 is a hack to make sure we
1620 # error out
1621 return updateresult(
1622 updated, merged, removed, max(len(unresolvedf), 1)
1623 )
1624 newactions = []
1625 for f, args, msg in mergeactions:
1626 if f in unresolvedf:
1627 newactions.append((f, args, msg))
1628 mergeactions = newactions
1629
1594
1630 try:
1595 try:
1631 # premerge
1596 # premerge
1632 tocomplete = []
1597 tocomplete = []
1633 for f, args, msg in mergeactions:
1598 for f, args, msg in mergeactions:
1634 repo.ui.debug(b" %s: %s -> m (premerge)\n" % (f, msg))
1599 repo.ui.debug(b" %s: %s -> m (premerge)\n" % (f, msg))
1635 progress.increment(item=f)
1600 progress.increment(item=f)
1636 if f == b'.hgsubstate': # subrepo states need updating
1601 if f == b'.hgsubstate': # subrepo states need updating
1637 subrepoutil.submerge(
1602 subrepoutil.submerge(
1638 repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels
1603 repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels
1639 )
1604 )
1640 continue
1605 continue
1641 wctx[f].audit()
1606 wctx[f].audit()
1642 complete, r = ms.preresolve(f, wctx)
1607 complete, r = ms.preresolve(f, wctx)
1643 if not complete:
1608 if not complete:
1644 numupdates += 1
1609 numupdates += 1
1645 tocomplete.append((f, args, msg))
1610 tocomplete.append((f, args, msg))
1646
1611
1647 # merge
1612 # merge
1648 for f, args, msg in tocomplete:
1613 for f, args, msg in tocomplete:
1649 repo.ui.debug(b" %s: %s -> m (merge)\n" % (f, msg))
1614 repo.ui.debug(b" %s: %s -> m (merge)\n" % (f, msg))
1650 progress.increment(item=f, total=numupdates)
1615 progress.increment(item=f, total=numupdates)
1651 ms.resolve(f, wctx)
1616 ms.resolve(f, wctx)
1652
1617
1653 finally:
1618 finally:
1654 ms.commit()
1619 ms.commit()
1655
1620
1656 unresolved = ms.unresolvedcount()
1621 unresolved = ms.unresolvedcount()
1657
1622
1658 if (
1659 usemergedriver
1660 and not unresolved
1661 and ms.mdstate() != mergestatemod.MERGE_DRIVER_STATE_SUCCESS
1662 ):
1663 if not driverconclude(repo, ms, wctx, labels=labels):
1664 # XXX setting unresolved to at least 1 is a hack to make sure we
1665 # error out
1666 unresolved = max(unresolved, 1)
1667
1668 ms.commit()
1669
1670 msupdated, msmerged, msremoved = ms.counts()
1623 msupdated, msmerged, msremoved = ms.counts()
1671 updated += msupdated
1624 updated += msupdated
1672 merged += msmerged
1625 merged += msmerged
1673 removed += msremoved
1626 removed += msremoved
1674
1627
1675 extraactions = ms.actions()
1628 extraactions = ms.actions()
1676 if extraactions:
1629 if extraactions:
1677 mfiles = {
1678 a[0] for a in mresult.getactions((mergestatemod.ACTION_MERGE,))
1679 }
1680 for k, acts in pycompat.iteritems(extraactions):
1630 for k, acts in pycompat.iteritems(extraactions):
1681 for a in acts:
1631 for a in acts:
1682 mresult.addfile(a[0], k, *a[1:])
1632 mresult.addfile(a[0], k, *a[1:])
1683 if k == mergestatemod.ACTION_GET and wantfiledata:
1633 if k == mergestatemod.ACTION_GET and wantfiledata:
1684 # no filedata until mergestate is updated to provide it
1634 # no filedata until mergestate is updated to provide it
1685 for a in acts:
1635 for a in acts:
1686 getfiledata[a[0]] = None
1636 getfiledata[a[0]] = None
1687 # Remove these files from actions[ACTION_MERGE] as well. This is
1688 # important because in recordupdates, files in actions[ACTION_MERGE]
1689 # are processed after files in other actions, and the merge driver
1690 # might add files to those actions via extraactions above. This can
1691 # lead to a file being recorded twice, with poor results. This is
1692 # especially problematic for actions[ACTION_REMOVE] (currently only
1693 # possible with the merge driver in the initial merge process;
1694 # interrupted merges don't go through this flow).
1695 #
1696 # The real fix here is to have indexes by both file and action so
1697 # that when the action for a file is changed it is automatically
1698 # reflected in the other action lists. But that involves a more
1699 # complex data structure, so this will do for now.
1700 #
1701 # We don't need to do the same operation for 'dc' and 'cd' because
1702 # those lists aren't consulted again.
1703 mfiles.difference_update(a[0] for a in acts)
1704
1705 for a in list(mresult.getactions((mergestatemod.ACTION_MERGE,))):
1706 if a[0] not in mfiles:
1707 mresult.removefile(a[0])
1708
1637
1709 progress.complete()
1638 progress.complete()
1710 assert len(getfiledata) == (
1639 assert len(getfiledata) == (
1711 mresult.len((mergestatemod.ACTION_GET,)) if wantfiledata else 0
1640 mresult.len((mergestatemod.ACTION_GET,)) if wantfiledata else 0
1712 )
1641 )
1713 return updateresult(updated, merged, removed, unresolved), getfiledata
1642 return updateresult(updated, merged, removed, unresolved), getfiledata
1714
1643
1715
1644
1716 def _advertisefsmonitor(repo, num_gets, p1node):
1645 def _advertisefsmonitor(repo, num_gets, p1node):
1717 # Advertise fsmonitor when its presence could be useful.
1646 # Advertise fsmonitor when its presence could be useful.
1718 #
1647 #
1719 # We only advertise when performing an update from an empty working
1648 # We only advertise when performing an update from an empty working
1720 # directory. This typically only occurs during initial clone.
1649 # directory. This typically only occurs during initial clone.
1721 #
1650 #
1722 # We give users a mechanism to disable the warning in case it is
1651 # We give users a mechanism to disable the warning in case it is
1723 # annoying.
1652 # annoying.
1724 #
1653 #
1725 # We only allow on Linux and MacOS because that's where fsmonitor is
1654 # We only allow on Linux and MacOS because that's where fsmonitor is
1726 # considered stable.
1655 # considered stable.
1727 fsmonitorwarning = repo.ui.configbool(b'fsmonitor', b'warn_when_unused')
1656 fsmonitorwarning = repo.ui.configbool(b'fsmonitor', b'warn_when_unused')
1728 fsmonitorthreshold = repo.ui.configint(
1657 fsmonitorthreshold = repo.ui.configint(
1729 b'fsmonitor', b'warn_update_file_count'
1658 b'fsmonitor', b'warn_update_file_count'
1730 )
1659 )
1731 # avoid cycle dirstate -> sparse -> merge -> dirstate
1660 # avoid cycle dirstate -> sparse -> merge -> dirstate
1732 from . import dirstate
1661 from . import dirstate
1733
1662
1734 if dirstate.rustmod is not None:
1663 if dirstate.rustmod is not None:
1735 # When using rust status, fsmonitor becomes necessary at higher sizes
1664 # When using rust status, fsmonitor becomes necessary at higher sizes
1736 fsmonitorthreshold = repo.ui.configint(
1665 fsmonitorthreshold = repo.ui.configint(
1737 b'fsmonitor', b'warn_update_file_count_rust',
1666 b'fsmonitor', b'warn_update_file_count_rust',
1738 )
1667 )
1739
1668
1740 try:
1669 try:
1741 # avoid cycle: extensions -> cmdutil -> merge
1670 # avoid cycle: extensions -> cmdutil -> merge
1742 from . import extensions
1671 from . import extensions
1743
1672
1744 extensions.find(b'fsmonitor')
1673 extensions.find(b'fsmonitor')
1745 fsmonitorenabled = repo.ui.config(b'fsmonitor', b'mode') != b'off'
1674 fsmonitorenabled = repo.ui.config(b'fsmonitor', b'mode') != b'off'
1746 # We intentionally don't look at whether fsmonitor has disabled
1675 # We intentionally don't look at whether fsmonitor has disabled
1747 # itself because a) fsmonitor may have already printed a warning
1676 # itself because a) fsmonitor may have already printed a warning
1748 # b) we only care about the config state here.
1677 # b) we only care about the config state here.
1749 except KeyError:
1678 except KeyError:
1750 fsmonitorenabled = False
1679 fsmonitorenabled = False
1751
1680
1752 if (
1681 if (
1753 fsmonitorwarning
1682 fsmonitorwarning
1754 and not fsmonitorenabled
1683 and not fsmonitorenabled
1755 and p1node == nullid
1684 and p1node == nullid
1756 and num_gets >= fsmonitorthreshold
1685 and num_gets >= fsmonitorthreshold
1757 and pycompat.sysplatform.startswith((b'linux', b'darwin'))
1686 and pycompat.sysplatform.startswith((b'linux', b'darwin'))
1758 ):
1687 ):
1759 repo.ui.warn(
1688 repo.ui.warn(
1760 _(
1689 _(
1761 b'(warning: large working directory being used without '
1690 b'(warning: large working directory being used without '
1762 b'fsmonitor enabled; enable fsmonitor to improve performance; '
1691 b'fsmonitor enabled; enable fsmonitor to improve performance; '
1763 b'see "hg help -e fsmonitor")\n'
1692 b'see "hg help -e fsmonitor")\n'
1764 )
1693 )
1765 )
1694 )
1766
1695
1767
1696
1768 UPDATECHECK_ABORT = b'abort' # handled at higher layers
1697 UPDATECHECK_ABORT = b'abort' # handled at higher layers
1769 UPDATECHECK_NONE = b'none'
1698 UPDATECHECK_NONE = b'none'
1770 UPDATECHECK_LINEAR = b'linear'
1699 UPDATECHECK_LINEAR = b'linear'
1771 UPDATECHECK_NO_CONFLICT = b'noconflict'
1700 UPDATECHECK_NO_CONFLICT = b'noconflict'
1772
1701
1773
1702
1774 def update(
1703 def update(
1775 repo,
1704 repo,
1776 node,
1705 node,
1777 branchmerge,
1706 branchmerge,
1778 force,
1707 force,
1779 ancestor=None,
1708 ancestor=None,
1780 mergeancestor=False,
1709 mergeancestor=False,
1781 labels=None,
1710 labels=None,
1782 matcher=None,
1711 matcher=None,
1783 mergeforce=False,
1712 mergeforce=False,
1784 updatedirstate=True,
1713 updatedirstate=True,
1785 updatecheck=None,
1714 updatecheck=None,
1786 wc=None,
1715 wc=None,
1787 ):
1716 ):
1788 """
1717 """
1789 Perform a merge between the working directory and the given node
1718 Perform a merge between the working directory and the given node
1790
1719
1791 node = the node to update to
1720 node = the node to update to
1792 branchmerge = whether to merge between branches
1721 branchmerge = whether to merge between branches
1793 force = whether to force branch merging or file overwriting
1722 force = whether to force branch merging or file overwriting
1794 matcher = a matcher to filter file lists (dirstate not updated)
1723 matcher = a matcher to filter file lists (dirstate not updated)
1795 mergeancestor = whether it is merging with an ancestor. If true,
1724 mergeancestor = whether it is merging with an ancestor. If true,
1796 we should accept the incoming changes for any prompts that occur.
1725 we should accept the incoming changes for any prompts that occur.
1797 If false, merging with an ancestor (fast-forward) is only allowed
1726 If false, merging with an ancestor (fast-forward) is only allowed
1798 between different named branches. This flag is used by rebase extension
1727 between different named branches. This flag is used by rebase extension
1799 as a temporary fix and should be avoided in general.
1728 as a temporary fix and should be avoided in general.
1800 labels = labels to use for base, local and other
1729 labels = labels to use for base, local and other
1801 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1730 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1802 this is True, then 'force' should be True as well.
1731 this is True, then 'force' should be True as well.
1803
1732
1804 The table below shows all the behaviors of the update command given the
1733 The table below shows all the behaviors of the update command given the
1805 -c/--check and -C/--clean or no options, whether the working directory is
1734 -c/--check and -C/--clean or no options, whether the working directory is
1806 dirty, whether a revision is specified, and the relationship of the parent
1735 dirty, whether a revision is specified, and the relationship of the parent
1807 rev to the target rev (linear or not). Match from top first. The -n
1736 rev to the target rev (linear or not). Match from top first. The -n
1808 option doesn't exist on the command line, but represents the
1737 option doesn't exist on the command line, but represents the
1809 experimental.updatecheck=noconflict option.
1738 experimental.updatecheck=noconflict option.
1810
1739
1811 This logic is tested by test-update-branches.t.
1740 This logic is tested by test-update-branches.t.
1812
1741
1813 -c -C -n -m dirty rev linear | result
1742 -c -C -n -m dirty rev linear | result
1814 y y * * * * * | (1)
1743 y y * * * * * | (1)
1815 y * y * * * * | (1)
1744 y * y * * * * | (1)
1816 y * * y * * * | (1)
1745 y * * y * * * | (1)
1817 * y y * * * * | (1)
1746 * y y * * * * | (1)
1818 * y * y * * * | (1)
1747 * y * y * * * | (1)
1819 * * y y * * * | (1)
1748 * * y y * * * | (1)
1820 * * * * * n n | x
1749 * * * * * n n | x
1821 * * * * n * * | ok
1750 * * * * n * * | ok
1822 n n n n y * y | merge
1751 n n n n y * y | merge
1823 n n n n y y n | (2)
1752 n n n n y y n | (2)
1824 n n n y y * * | merge
1753 n n n y y * * | merge
1825 n n y n y * * | merge if no conflict
1754 n n y n y * * | merge if no conflict
1826 n y n n y * * | discard
1755 n y n n y * * | discard
1827 y n n n y * * | (3)
1756 y n n n y * * | (3)
1828
1757
1829 x = can't happen
1758 x = can't happen
1830 * = don't-care
1759 * = don't-care
1831 1 = incompatible options (checked in commands.py)
1760 1 = incompatible options (checked in commands.py)
1832 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1761 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1833 3 = abort: uncommitted changes (checked in commands.py)
1762 3 = abort: uncommitted changes (checked in commands.py)
1834
1763
1835 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1764 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1836 to repo[None] if None is passed.
1765 to repo[None] if None is passed.
1837
1766
1838 Return the same tuple as applyupdates().
1767 Return the same tuple as applyupdates().
1839 """
1768 """
1840 # Avoid cycle.
1769 # Avoid cycle.
1841 from . import sparse
1770 from . import sparse
1842
1771
1843 # This function used to find the default destination if node was None, but
1772 # This function used to find the default destination if node was None, but
1844 # that's now in destutil.py.
1773 # that's now in destutil.py.
1845 assert node is not None
1774 assert node is not None
1846 if not branchmerge and not force:
1775 if not branchmerge and not force:
1847 # TODO: remove the default once all callers that pass branchmerge=False
1776 # TODO: remove the default once all callers that pass branchmerge=False
1848 # and force=False pass a value for updatecheck. We may want to allow
1777 # and force=False pass a value for updatecheck. We may want to allow
1849 # updatecheck='abort' to better suppport some of these callers.
1778 # updatecheck='abort' to better suppport some of these callers.
1850 if updatecheck is None:
1779 if updatecheck is None:
1851 updatecheck = UPDATECHECK_LINEAR
1780 updatecheck = UPDATECHECK_LINEAR
1852 if updatecheck not in (
1781 if updatecheck not in (
1853 UPDATECHECK_NONE,
1782 UPDATECHECK_NONE,
1854 UPDATECHECK_LINEAR,
1783 UPDATECHECK_LINEAR,
1855 UPDATECHECK_NO_CONFLICT,
1784 UPDATECHECK_NO_CONFLICT,
1856 ):
1785 ):
1857 raise ValueError(
1786 raise ValueError(
1858 r'Invalid updatecheck %r (can accept %r)'
1787 r'Invalid updatecheck %r (can accept %r)'
1859 % (
1788 % (
1860 updatecheck,
1789 updatecheck,
1861 (
1790 (
1862 UPDATECHECK_NONE,
1791 UPDATECHECK_NONE,
1863 UPDATECHECK_LINEAR,
1792 UPDATECHECK_LINEAR,
1864 UPDATECHECK_NO_CONFLICT,
1793 UPDATECHECK_NO_CONFLICT,
1865 ),
1794 ),
1866 )
1795 )
1867 )
1796 )
1868 if wc is not None and wc.isinmemory():
1797 if wc is not None and wc.isinmemory():
1869 maybe_wlock = util.nullcontextmanager()
1798 maybe_wlock = util.nullcontextmanager()
1870 else:
1799 else:
1871 maybe_wlock = repo.wlock()
1800 maybe_wlock = repo.wlock()
1872 with maybe_wlock:
1801 with maybe_wlock:
1873 if wc is None:
1802 if wc is None:
1874 wc = repo[None]
1803 wc = repo[None]
1875 pl = wc.parents()
1804 pl = wc.parents()
1876 p1 = pl[0]
1805 p1 = pl[0]
1877 p2 = repo[node]
1806 p2 = repo[node]
1878 if ancestor is not None:
1807 if ancestor is not None:
1879 pas = [repo[ancestor]]
1808 pas = [repo[ancestor]]
1880 else:
1809 else:
1881 if repo.ui.configlist(b'merge', b'preferancestor') == [b'*']:
1810 if repo.ui.configlist(b'merge', b'preferancestor') == [b'*']:
1882 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1811 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1883 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1812 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1884 else:
1813 else:
1885 pas = [p1.ancestor(p2, warn=branchmerge)]
1814 pas = [p1.ancestor(p2, warn=branchmerge)]
1886
1815
1887 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
1816 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
1888
1817
1889 overwrite = force and not branchmerge
1818 overwrite = force and not branchmerge
1890 ### check phase
1819 ### check phase
1891 if not overwrite:
1820 if not overwrite:
1892 if len(pl) > 1:
1821 if len(pl) > 1:
1893 raise error.Abort(_(b"outstanding uncommitted merge"))
1822 raise error.Abort(_(b"outstanding uncommitted merge"))
1894 ms = wc.mergestate()
1823 ms = wc.mergestate()
1895 if list(ms.unresolved()):
1824 if list(ms.unresolved()):
1896 raise error.Abort(
1825 raise error.Abort(
1897 _(b"outstanding merge conflicts"),
1826 _(b"outstanding merge conflicts"),
1898 hint=_(b"use 'hg resolve' to resolve"),
1827 hint=_(b"use 'hg resolve' to resolve"),
1899 )
1828 )
1900 if branchmerge:
1829 if branchmerge:
1901 if pas == [p2]:
1830 if pas == [p2]:
1902 raise error.Abort(
1831 raise error.Abort(
1903 _(
1832 _(
1904 b"merging with a working directory ancestor"
1833 b"merging with a working directory ancestor"
1905 b" has no effect"
1834 b" has no effect"
1906 )
1835 )
1907 )
1836 )
1908 elif pas == [p1]:
1837 elif pas == [p1]:
1909 if not mergeancestor and wc.branch() == p2.branch():
1838 if not mergeancestor and wc.branch() == p2.branch():
1910 raise error.Abort(
1839 raise error.Abort(
1911 _(b"nothing to merge"),
1840 _(b"nothing to merge"),
1912 hint=_(b"use 'hg update' or check 'hg heads'"),
1841 hint=_(b"use 'hg update' or check 'hg heads'"),
1913 )
1842 )
1914 if not force and (wc.files() or wc.deleted()):
1843 if not force and (wc.files() or wc.deleted()):
1915 raise error.Abort(
1844 raise error.Abort(
1916 _(b"uncommitted changes"),
1845 _(b"uncommitted changes"),
1917 hint=_(b"use 'hg status' to list changes"),
1846 hint=_(b"use 'hg status' to list changes"),
1918 )
1847 )
1919 if not wc.isinmemory():
1848 if not wc.isinmemory():
1920 for s in sorted(wc.substate):
1849 for s in sorted(wc.substate):
1921 wc.sub(s).bailifchanged()
1850 wc.sub(s).bailifchanged()
1922
1851
1923 elif not overwrite:
1852 elif not overwrite:
1924 if p1 == p2: # no-op update
1853 if p1 == p2: # no-op update
1925 # call the hooks and exit early
1854 # call the hooks and exit early
1926 repo.hook(b'preupdate', throw=True, parent1=xp2, parent2=b'')
1855 repo.hook(b'preupdate', throw=True, parent1=xp2, parent2=b'')
1927 repo.hook(b'update', parent1=xp2, parent2=b'', error=0)
1856 repo.hook(b'update', parent1=xp2, parent2=b'', error=0)
1928 return updateresult(0, 0, 0, 0)
1857 return updateresult(0, 0, 0, 0)
1929
1858
1930 if updatecheck == UPDATECHECK_LINEAR and pas not in (
1859 if updatecheck == UPDATECHECK_LINEAR and pas not in (
1931 [p1],
1860 [p1],
1932 [p2],
1861 [p2],
1933 ): # nonlinear
1862 ): # nonlinear
1934 dirty = wc.dirty(missing=True)
1863 dirty = wc.dirty(missing=True)
1935 if dirty:
1864 if dirty:
1936 # Branching is a bit strange to ensure we do the minimal
1865 # Branching is a bit strange to ensure we do the minimal
1937 # amount of call to obsutil.foreground.
1866 # amount of call to obsutil.foreground.
1938 foreground = obsutil.foreground(repo, [p1.node()])
1867 foreground = obsutil.foreground(repo, [p1.node()])
1939 # note: the <node> variable contains a random identifier
1868 # note: the <node> variable contains a random identifier
1940 if repo[node].node() in foreground:
1869 if repo[node].node() in foreground:
1941 pass # allow updating to successors
1870 pass # allow updating to successors
1942 else:
1871 else:
1943 msg = _(b"uncommitted changes")
1872 msg = _(b"uncommitted changes")
1944 hint = _(b"commit or update --clean to discard changes")
1873 hint = _(b"commit or update --clean to discard changes")
1945 raise error.UpdateAbort(msg, hint=hint)
1874 raise error.UpdateAbort(msg, hint=hint)
1946 else:
1875 else:
1947 # Allow jumping branches if clean and specific rev given
1876 # Allow jumping branches if clean and specific rev given
1948 pass
1877 pass
1949
1878
1950 if overwrite:
1879 if overwrite:
1951 pas = [wc]
1880 pas = [wc]
1952 elif not branchmerge:
1881 elif not branchmerge:
1953 pas = [p1]
1882 pas = [p1]
1954
1883
1955 # deprecated config: merge.followcopies
1884 # deprecated config: merge.followcopies
1956 followcopies = repo.ui.configbool(b'merge', b'followcopies')
1885 followcopies = repo.ui.configbool(b'merge', b'followcopies')
1957 if overwrite:
1886 if overwrite:
1958 followcopies = False
1887 followcopies = False
1959 elif not pas[0]:
1888 elif not pas[0]:
1960 followcopies = False
1889 followcopies = False
1961 if not branchmerge and not wc.dirty(missing=True):
1890 if not branchmerge and not wc.dirty(missing=True):
1962 followcopies = False
1891 followcopies = False
1963
1892
1964 ### calculate phase
1893 ### calculate phase
1965 mresult = calculateupdates(
1894 mresult = calculateupdates(
1966 repo,
1895 repo,
1967 wc,
1896 wc,
1968 p2,
1897 p2,
1969 pas,
1898 pas,
1970 branchmerge,
1899 branchmerge,
1971 force,
1900 force,
1972 mergeancestor,
1901 mergeancestor,
1973 followcopies,
1902 followcopies,
1974 matcher=matcher,
1903 matcher=matcher,
1975 mergeforce=mergeforce,
1904 mergeforce=mergeforce,
1976 )
1905 )
1977
1906
1978 if updatecheck == UPDATECHECK_NO_CONFLICT:
1907 if updatecheck == UPDATECHECK_NO_CONFLICT:
1979 if mresult.hasconflicts():
1908 if mresult.hasconflicts():
1980 msg = _(b"conflicting changes")
1909 msg = _(b"conflicting changes")
1981 hint = _(b"commit or update --clean to discard changes")
1910 hint = _(b"commit or update --clean to discard changes")
1982 raise error.Abort(msg, hint=hint)
1911 raise error.Abort(msg, hint=hint)
1983
1912
1984 # Prompt and create actions. Most of this is in the resolve phase
1913 # Prompt and create actions. Most of this is in the resolve phase
1985 # already, but we can't handle .hgsubstate in filemerge or
1914 # already, but we can't handle .hgsubstate in filemerge or
1986 # subrepoutil.submerge yet so we have to keep prompting for it.
1915 # subrepoutil.submerge yet so we have to keep prompting for it.
1987 vals = mresult.getfile(b'.hgsubstate')
1916 vals = mresult.getfile(b'.hgsubstate')
1988 if vals:
1917 if vals:
1989 f = b'.hgsubstate'
1918 f = b'.hgsubstate'
1990 m, args, msg = vals
1919 m, args, msg = vals
1991 prompts = filemerge.partextras(labels)
1920 prompts = filemerge.partextras(labels)
1992 prompts[b'f'] = f
1921 prompts[b'f'] = f
1993 if m == mergestatemod.ACTION_CHANGED_DELETED:
1922 if m == mergestatemod.ACTION_CHANGED_DELETED:
1994 if repo.ui.promptchoice(
1923 if repo.ui.promptchoice(
1995 _(
1924 _(
1996 b"local%(l)s changed %(f)s which other%(o)s deleted\n"
1925 b"local%(l)s changed %(f)s which other%(o)s deleted\n"
1997 b"use (c)hanged version or (d)elete?"
1926 b"use (c)hanged version or (d)elete?"
1998 b"$$ &Changed $$ &Delete"
1927 b"$$ &Changed $$ &Delete"
1999 )
1928 )
2000 % prompts,
1929 % prompts,
2001 0,
1930 0,
2002 ):
1931 ):
2003 mresult.addfile(
1932 mresult.addfile(
2004 f, mergestatemod.ACTION_REMOVE, None, b'prompt delete',
1933 f, mergestatemod.ACTION_REMOVE, None, b'prompt delete',
2005 )
1934 )
2006 elif f in p1:
1935 elif f in p1:
2007 mresult.addfile(
1936 mresult.addfile(
2008 f,
1937 f,
2009 mergestatemod.ACTION_ADD_MODIFIED,
1938 mergestatemod.ACTION_ADD_MODIFIED,
2010 None,
1939 None,
2011 b'prompt keep',
1940 b'prompt keep',
2012 )
1941 )
2013 else:
1942 else:
2014 mresult.addfile(
1943 mresult.addfile(
2015 f, mergestatemod.ACTION_ADD, None, b'prompt keep',
1944 f, mergestatemod.ACTION_ADD, None, b'prompt keep',
2016 )
1945 )
2017 elif m == mergestatemod.ACTION_DELETED_CHANGED:
1946 elif m == mergestatemod.ACTION_DELETED_CHANGED:
2018 f1, f2, fa, move, anc = args
1947 f1, f2, fa, move, anc = args
2019 flags = p2[f2].flags()
1948 flags = p2[f2].flags()
2020 if (
1949 if (
2021 repo.ui.promptchoice(
1950 repo.ui.promptchoice(
2022 _(
1951 _(
2023 b"other%(o)s changed %(f)s which local%(l)s deleted\n"
1952 b"other%(o)s changed %(f)s which local%(l)s deleted\n"
2024 b"use (c)hanged version or leave (d)eleted?"
1953 b"use (c)hanged version or leave (d)eleted?"
2025 b"$$ &Changed $$ &Deleted"
1954 b"$$ &Changed $$ &Deleted"
2026 )
1955 )
2027 % prompts,
1956 % prompts,
2028 0,
1957 0,
2029 )
1958 )
2030 == 0
1959 == 0
2031 ):
1960 ):
2032 mresult.addfile(
1961 mresult.addfile(
2033 f,
1962 f,
2034 mergestatemod.ACTION_GET,
1963 mergestatemod.ACTION_GET,
2035 (flags, False),
1964 (flags, False),
2036 b'prompt recreating',
1965 b'prompt recreating',
2037 )
1966 )
2038 else:
1967 else:
2039 mresult.removefile(f)
1968 mresult.removefile(f)
2040
1969
2041 if not util.fscasesensitive(repo.path):
1970 if not util.fscasesensitive(repo.path):
2042 # check collision between files only in p2 for clean update
1971 # check collision between files only in p2 for clean update
2043 if not branchmerge and (
1972 if not branchmerge and (
2044 force or not wc.dirty(missing=True, branch=False)
1973 force or not wc.dirty(missing=True, branch=False)
2045 ):
1974 ):
2046 _checkcollision(repo, p2.manifest(), None)
1975 _checkcollision(repo, p2.manifest(), None)
2047 else:
1976 else:
2048 _checkcollision(repo, wc.manifest(), mresult)
1977 _checkcollision(repo, wc.manifest(), mresult)
2049
1978
2050 # divergent renames
1979 # divergent renames
2051 for f, fl in sorted(pycompat.iteritems(mresult.diverge)):
1980 for f, fl in sorted(pycompat.iteritems(mresult.diverge)):
2052 repo.ui.warn(
1981 repo.ui.warn(
2053 _(
1982 _(
2054 b"note: possible conflict - %s was renamed "
1983 b"note: possible conflict - %s was renamed "
2055 b"multiple times to:\n"
1984 b"multiple times to:\n"
2056 )
1985 )
2057 % f
1986 % f
2058 )
1987 )
2059 for nf in sorted(fl):
1988 for nf in sorted(fl):
2060 repo.ui.warn(b" %s\n" % nf)
1989 repo.ui.warn(b" %s\n" % nf)
2061
1990
2062 # rename and delete
1991 # rename and delete
2063 for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)):
1992 for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)):
2064 repo.ui.warn(
1993 repo.ui.warn(
2065 _(
1994 _(
2066 b"note: possible conflict - %s was deleted "
1995 b"note: possible conflict - %s was deleted "
2067 b"and renamed to:\n"
1996 b"and renamed to:\n"
2068 )
1997 )
2069 % f
1998 % f
2070 )
1999 )
2071 for nf in sorted(fl):
2000 for nf in sorted(fl):
2072 repo.ui.warn(b" %s\n" % nf)
2001 repo.ui.warn(b" %s\n" % nf)
2073
2002
2074 ### apply phase
2003 ### apply phase
2075 if not branchmerge: # just jump to the new rev
2004 if not branchmerge: # just jump to the new rev
2076 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, b''
2005 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, b''
2077 # If we're doing a partial update, we need to skip updating
2006 # If we're doing a partial update, we need to skip updating
2078 # the dirstate.
2007 # the dirstate.
2079 always = matcher is None or matcher.always()
2008 always = matcher is None or matcher.always()
2080 updatedirstate = updatedirstate and always and not wc.isinmemory()
2009 updatedirstate = updatedirstate and always and not wc.isinmemory()
2081 if updatedirstate:
2010 if updatedirstate:
2082 repo.hook(b'preupdate', throw=True, parent1=xp1, parent2=xp2)
2011 repo.hook(b'preupdate', throw=True, parent1=xp1, parent2=xp2)
2083 # note that we're in the middle of an update
2012 # note that we're in the middle of an update
2084 repo.vfs.write(b'updatestate', p2.hex())
2013 repo.vfs.write(b'updatestate', p2.hex())
2085
2014
2086 _advertisefsmonitor(
2015 _advertisefsmonitor(
2087 repo, mresult.len((mergestatemod.ACTION_GET,)), p1.node()
2016 repo, mresult.len((mergestatemod.ACTION_GET,)), p1.node()
2088 )
2017 )
2089
2018
2090 wantfiledata = updatedirstate and not branchmerge
2019 wantfiledata = updatedirstate and not branchmerge
2091 stats, getfiledata = applyupdates(
2020 stats, getfiledata = applyupdates(
2092 repo, mresult, wc, p2, overwrite, wantfiledata, labels=labels,
2021 repo, mresult, wc, p2, overwrite, wantfiledata, labels=labels,
2093 )
2022 )
2094
2023
2095 if updatedirstate:
2024 if updatedirstate:
2096 with repo.dirstate.parentchange():
2025 with repo.dirstate.parentchange():
2097 repo.setparents(fp1, fp2)
2026 repo.setparents(fp1, fp2)
2098 mergestatemod.recordupdates(
2027 mergestatemod.recordupdates(
2099 repo, mresult.actionsdict, branchmerge, getfiledata
2028 repo, mresult.actionsdict, branchmerge, getfiledata
2100 )
2029 )
2101 # update completed, clear state
2030 # update completed, clear state
2102 util.unlink(repo.vfs.join(b'updatestate'))
2031 util.unlink(repo.vfs.join(b'updatestate'))
2103
2032
2104 if not branchmerge:
2033 if not branchmerge:
2105 repo.dirstate.setbranch(p2.branch())
2034 repo.dirstate.setbranch(p2.branch())
2106
2035
2107 # If we're updating to a location, clean up any stale temporary includes
2036 # If we're updating to a location, clean up any stale temporary includes
2108 # (ex: this happens during hg rebase --abort).
2037 # (ex: this happens during hg rebase --abort).
2109 if not branchmerge:
2038 if not branchmerge:
2110 sparse.prunetemporaryincludes(repo)
2039 sparse.prunetemporaryincludes(repo)
2111
2040
2112 if updatedirstate:
2041 if updatedirstate:
2113 repo.hook(
2042 repo.hook(
2114 b'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount
2043 b'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount
2115 )
2044 )
2116 return stats
2045 return stats
2117
2046
2118
2047
2119 def merge(ctx, labels=None, force=False, wc=None):
2048 def merge(ctx, labels=None, force=False, wc=None):
2120 """Merge another topological branch into the working copy.
2049 """Merge another topological branch into the working copy.
2121
2050
2122 force = whether the merge was run with 'merge --force' (deprecated)
2051 force = whether the merge was run with 'merge --force' (deprecated)
2123 """
2052 """
2124
2053
2125 return update(
2054 return update(
2126 ctx.repo(),
2055 ctx.repo(),
2127 ctx.rev(),
2056 ctx.rev(),
2128 labels=labels,
2057 labels=labels,
2129 branchmerge=True,
2058 branchmerge=True,
2130 force=force,
2059 force=force,
2131 mergeforce=force,
2060 mergeforce=force,
2132 wc=wc,
2061 wc=wc,
2133 )
2062 )
2134
2063
2135
2064
2136 def clean_update(ctx, wc=None):
2065 def clean_update(ctx, wc=None):
2137 """Do a clean update to the given commit.
2066 """Do a clean update to the given commit.
2138
2067
2139 This involves updating to the commit and discarding any changes in the
2068 This involves updating to the commit and discarding any changes in the
2140 working copy.
2069 working copy.
2141 """
2070 """
2142 return update(ctx.repo(), ctx.rev(), branchmerge=False, force=True, wc=wc)
2071 return update(ctx.repo(), ctx.rev(), branchmerge=False, force=True, wc=wc)
2143
2072
2144
2073
2145 def revert_to(ctx, matcher=None, wc=None):
2074 def revert_to(ctx, matcher=None, wc=None):
2146 """Revert the working copy to the given commit.
2075 """Revert the working copy to the given commit.
2147
2076
2148 The working copy will keep its current parent(s) but its content will
2077 The working copy will keep its current parent(s) but its content will
2149 be the same as in the given commit.
2078 be the same as in the given commit.
2150 """
2079 """
2151
2080
2152 return update(
2081 return update(
2153 ctx.repo(),
2082 ctx.repo(),
2154 ctx.rev(),
2083 ctx.rev(),
2155 branchmerge=False,
2084 branchmerge=False,
2156 force=True,
2085 force=True,
2157 updatedirstate=False,
2086 updatedirstate=False,
2158 matcher=matcher,
2087 matcher=matcher,
2159 wc=wc,
2088 wc=wc,
2160 )
2089 )
2161
2090
2162
2091
2163 def graft(
2092 def graft(
2164 repo,
2093 repo,
2165 ctx,
2094 ctx,
2166 base=None,
2095 base=None,
2167 labels=None,
2096 labels=None,
2168 keepparent=False,
2097 keepparent=False,
2169 keepconflictparent=False,
2098 keepconflictparent=False,
2170 wctx=None,
2099 wctx=None,
2171 ):
2100 ):
2172 """Do a graft-like merge.
2101 """Do a graft-like merge.
2173
2102
2174 This is a merge where the merge ancestor is chosen such that one
2103 This is a merge where the merge ancestor is chosen such that one
2175 or more changesets are grafted onto the current changeset. In
2104 or more changesets are grafted onto the current changeset. In
2176 addition to the merge, this fixes up the dirstate to include only
2105 addition to the merge, this fixes up the dirstate to include only
2177 a single parent (if keepparent is False) and tries to duplicate any
2106 a single parent (if keepparent is False) and tries to duplicate any
2178 renames/copies appropriately.
2107 renames/copies appropriately.
2179
2108
2180 ctx - changeset to rebase
2109 ctx - changeset to rebase
2181 base - merge base, or ctx.p1() if not specified
2110 base - merge base, or ctx.p1() if not specified
2182 labels - merge labels eg ['local', 'graft']
2111 labels - merge labels eg ['local', 'graft']
2183 keepparent - keep second parent if any
2112 keepparent - keep second parent if any
2184 keepconflictparent - if unresolved, keep parent used for the merge
2113 keepconflictparent - if unresolved, keep parent used for the merge
2185
2114
2186 """
2115 """
2187 # If we're grafting a descendant onto an ancestor, be sure to pass
2116 # If we're grafting a descendant onto an ancestor, be sure to pass
2188 # mergeancestor=True to update. This does two things: 1) allows the merge if
2117 # mergeancestor=True to update. This does two things: 1) allows the merge if
2189 # the destination is the same as the parent of the ctx (so we can use graft
2118 # the destination is the same as the parent of the ctx (so we can use graft
2190 # to copy commits), and 2) informs update that the incoming changes are
2119 # to copy commits), and 2) informs update that the incoming changes are
2191 # newer than the destination so it doesn't prompt about "remote changed foo
2120 # newer than the destination so it doesn't prompt about "remote changed foo
2192 # which local deleted".
2121 # which local deleted".
2193 # We also pass mergeancestor=True when base is the same revision as p1. 2)
2122 # We also pass mergeancestor=True when base is the same revision as p1. 2)
2194 # doesn't matter as there can't possibly be conflicts, but 1) is necessary.
2123 # doesn't matter as there can't possibly be conflicts, but 1) is necessary.
2195 wctx = wctx or repo[None]
2124 wctx = wctx or repo[None]
2196 pctx = wctx.p1()
2125 pctx = wctx.p1()
2197 base = base or ctx.p1()
2126 base = base or ctx.p1()
2198 mergeancestor = (
2127 mergeancestor = (
2199 repo.changelog.isancestor(pctx.node(), ctx.node())
2128 repo.changelog.isancestor(pctx.node(), ctx.node())
2200 or pctx.rev() == base.rev()
2129 or pctx.rev() == base.rev()
2201 )
2130 )
2202
2131
2203 stats = update(
2132 stats = update(
2204 repo,
2133 repo,
2205 ctx.node(),
2134 ctx.node(),
2206 True,
2135 True,
2207 True,
2136 True,
2208 base.node(),
2137 base.node(),
2209 mergeancestor=mergeancestor,
2138 mergeancestor=mergeancestor,
2210 labels=labels,
2139 labels=labels,
2211 wc=wctx,
2140 wc=wctx,
2212 )
2141 )
2213
2142
2214 if keepconflictparent and stats.unresolvedcount:
2143 if keepconflictparent and stats.unresolvedcount:
2215 pother = ctx.node()
2144 pother = ctx.node()
2216 else:
2145 else:
2217 pother = nullid
2146 pother = nullid
2218 parents = ctx.parents()
2147 parents = ctx.parents()
2219 if keepparent and len(parents) == 2 and base in parents:
2148 if keepparent and len(parents) == 2 and base in parents:
2220 parents.remove(base)
2149 parents.remove(base)
2221 pother = parents[0].node()
2150 pother = parents[0].node()
2222 # Never set both parents equal to each other
2151 # Never set both parents equal to each other
2223 if pother == pctx.node():
2152 if pother == pctx.node():
2224 pother = nullid
2153 pother = nullid
2225
2154
2226 if wctx.isinmemory():
2155 if wctx.isinmemory():
2227 wctx.setparents(pctx.node(), pother)
2156 wctx.setparents(pctx.node(), pother)
2228 # fix up dirstate for copies and renames
2157 # fix up dirstate for copies and renames
2229 copies.graftcopies(wctx, ctx, base)
2158 copies.graftcopies(wctx, ctx, base)
2230 else:
2159 else:
2231 with repo.dirstate.parentchange():
2160 with repo.dirstate.parentchange():
2232 repo.setparents(pctx.node(), pother)
2161 repo.setparents(pctx.node(), pother)
2233 repo.dirstate.write(repo.currenttransaction())
2162 repo.dirstate.write(repo.currenttransaction())
2234 # fix up dirstate for copies and renames
2163 # fix up dirstate for copies and renames
2235 copies.graftcopies(wctx, ctx, base)
2164 copies.graftcopies(wctx, ctx, base)
2236 return stats
2165 return stats
2237
2166
2238
2167
2239 def purge(
2168 def purge(
2240 repo,
2169 repo,
2241 matcher,
2170 matcher,
2242 unknown=True,
2171 unknown=True,
2243 ignored=False,
2172 ignored=False,
2244 removeemptydirs=True,
2173 removeemptydirs=True,
2245 removefiles=True,
2174 removefiles=True,
2246 abortonerror=False,
2175 abortonerror=False,
2247 noop=False,
2176 noop=False,
2248 ):
2177 ):
2249 """Purge the working directory of untracked files.
2178 """Purge the working directory of untracked files.
2250
2179
2251 ``matcher`` is a matcher configured to scan the working directory -
2180 ``matcher`` is a matcher configured to scan the working directory -
2252 potentially a subset.
2181 potentially a subset.
2253
2182
2254 ``unknown`` controls whether unknown files should be purged.
2183 ``unknown`` controls whether unknown files should be purged.
2255
2184
2256 ``ignored`` controls whether ignored files should be purged.
2185 ``ignored`` controls whether ignored files should be purged.
2257
2186
2258 ``removeemptydirs`` controls whether empty directories should be removed.
2187 ``removeemptydirs`` controls whether empty directories should be removed.
2259
2188
2260 ``removefiles`` controls whether files are removed.
2189 ``removefiles`` controls whether files are removed.
2261
2190
2262 ``abortonerror`` causes an exception to be raised if an error occurs
2191 ``abortonerror`` causes an exception to be raised if an error occurs
2263 deleting a file or directory.
2192 deleting a file or directory.
2264
2193
2265 ``noop`` controls whether to actually remove files. If not defined, actions
2194 ``noop`` controls whether to actually remove files. If not defined, actions
2266 will be taken.
2195 will be taken.
2267
2196
2268 Returns an iterable of relative paths in the working directory that were
2197 Returns an iterable of relative paths in the working directory that were
2269 or would be removed.
2198 or would be removed.
2270 """
2199 """
2271
2200
2272 def remove(removefn, path):
2201 def remove(removefn, path):
2273 try:
2202 try:
2274 removefn(path)
2203 removefn(path)
2275 except OSError:
2204 except OSError:
2276 m = _(b'%s cannot be removed') % path
2205 m = _(b'%s cannot be removed') % path
2277 if abortonerror:
2206 if abortonerror:
2278 raise error.Abort(m)
2207 raise error.Abort(m)
2279 else:
2208 else:
2280 repo.ui.warn(_(b'warning: %s\n') % m)
2209 repo.ui.warn(_(b'warning: %s\n') % m)
2281
2210
2282 # There's no API to copy a matcher. So mutate the passed matcher and
2211 # There's no API to copy a matcher. So mutate the passed matcher and
2283 # restore it when we're done.
2212 # restore it when we're done.
2284 oldtraversedir = matcher.traversedir
2213 oldtraversedir = matcher.traversedir
2285
2214
2286 res = []
2215 res = []
2287
2216
2288 try:
2217 try:
2289 if removeemptydirs:
2218 if removeemptydirs:
2290 directories = []
2219 directories = []
2291 matcher.traversedir = directories.append
2220 matcher.traversedir = directories.append
2292
2221
2293 status = repo.status(match=matcher, ignored=ignored, unknown=unknown)
2222 status = repo.status(match=matcher, ignored=ignored, unknown=unknown)
2294
2223
2295 if removefiles:
2224 if removefiles:
2296 for f in sorted(status.unknown + status.ignored):
2225 for f in sorted(status.unknown + status.ignored):
2297 if not noop:
2226 if not noop:
2298 repo.ui.note(_(b'removing file %s\n') % f)
2227 repo.ui.note(_(b'removing file %s\n') % f)
2299 remove(repo.wvfs.unlink, f)
2228 remove(repo.wvfs.unlink, f)
2300 res.append(f)
2229 res.append(f)
2301
2230
2302 if removeemptydirs:
2231 if removeemptydirs:
2303 for f in sorted(directories, reverse=True):
2232 for f in sorted(directories, reverse=True):
2304 if matcher(f) and not repo.wvfs.listdir(f):
2233 if matcher(f) and not repo.wvfs.listdir(f):
2305 if not noop:
2234 if not noop:
2306 repo.ui.note(_(b'removing directory %s\n') % f)
2235 repo.ui.note(_(b'removing directory %s\n') % f)
2307 remove(repo.wvfs.rmdir, f)
2236 remove(repo.wvfs.rmdir, f)
2308 res.append(f)
2237 res.append(f)
2309
2238
2310 return res
2239 return res
2311
2240
2312 finally:
2241 finally:
2313 matcher.traversedir = oldtraversedir
2242 matcher.traversedir = oldtraversedir
@@ -1,926 +1,821 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import collections
3 import collections
4 import errno
4 import errno
5 import shutil
5 import shutil
6 import struct
6 import struct
7
7
8 from .i18n import _
8 from .i18n import _
9 from .node import (
9 from .node import (
10 bin,
10 bin,
11 hex,
11 hex,
12 nullhex,
12 nullhex,
13 nullid,
13 nullid,
14 )
14 )
15 from . import (
15 from . import (
16 error,
16 error,
17 filemerge,
17 filemerge,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21 from .utils import hashutil
21 from .utils import hashutil
22
22
23 _pack = struct.pack
23 _pack = struct.pack
24 _unpack = struct.unpack
24 _unpack = struct.unpack
25
25
26
26
27 def _droponode(data):
27 def _droponode(data):
28 # used for compatibility for v1
28 # used for compatibility for v1
29 bits = data.split(b'\0')
29 bits = data.split(b'\0')
30 bits = bits[:-2] + bits[-1:]
30 bits = bits[:-2] + bits[-1:]
31 return b'\0'.join(bits)
31 return b'\0'.join(bits)
32
32
33
33
34 def _filectxorabsent(hexnode, ctx, f):
34 def _filectxorabsent(hexnode, ctx, f):
35 if hexnode == nullhex:
35 if hexnode == nullhex:
36 return filemerge.absentfilectx(ctx, f)
36 return filemerge.absentfilectx(ctx, f)
37 else:
37 else:
38 return ctx[f]
38 return ctx[f]
39
39
40
40
41 # Merge state record types. See ``mergestate`` docs for more.
41 # Merge state record types. See ``mergestate`` docs for more.
42
42
43 ####
43 ####
44 # merge records which records metadata about a current merge
44 # merge records which records metadata about a current merge
45 # exists only once in a mergestate
45 # exists only once in a mergestate
46 #####
46 #####
47 RECORD_LOCAL = b'L'
47 RECORD_LOCAL = b'L'
48 RECORD_OTHER = b'O'
48 RECORD_OTHER = b'O'
49 # record merge labels
49 # record merge labels
50 RECORD_LABELS = b'l'
50 RECORD_LABELS = b'l'
51 # store info about merge driver used and it's state
52 RECORD_MERGE_DRIVER_STATE = b'm'
53
51
54 #####
52 #####
55 # record extra information about files, with one entry containing info about one
53 # record extra information about files, with one entry containing info about one
56 # file. Hence, multiple of them can exists
54 # file. Hence, multiple of them can exists
57 #####
55 #####
58 RECORD_FILE_VALUES = b'f'
56 RECORD_FILE_VALUES = b'f'
59
57
60 #####
58 #####
61 # merge records which represents state of individual merges of files/folders
59 # merge records which represents state of individual merges of files/folders
62 # These are top level records for each entry containing merge related info.
60 # These are top level records for each entry containing merge related info.
63 # Each record of these has info about one file. Hence multiple of them can
61 # Each record of these has info about one file. Hence multiple of them can
64 # exists
62 # exists
65 #####
63 #####
66 RECORD_MERGED = b'F'
64 RECORD_MERGED = b'F'
67 RECORD_CHANGEDELETE_CONFLICT = b'C'
65 RECORD_CHANGEDELETE_CONFLICT = b'C'
68 RECORD_MERGE_DRIVER_MERGE = b'D'
69 # the path was dir on one side of merge and file on another
66 # the path was dir on one side of merge and file on another
70 RECORD_PATH_CONFLICT = b'P'
67 RECORD_PATH_CONFLICT = b'P'
71
68
72 #####
69 #####
73 # possible state which a merge entry can have. These are stored inside top-level
70 # possible state which a merge entry can have. These are stored inside top-level
74 # merge records mentioned just above.
71 # merge records mentioned just above.
75 #####
72 #####
76 MERGE_RECORD_UNRESOLVED = b'u'
73 MERGE_RECORD_UNRESOLVED = b'u'
77 MERGE_RECORD_RESOLVED = b'r'
74 MERGE_RECORD_RESOLVED = b'r'
78 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
75 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
79 MERGE_RECORD_RESOLVED_PATH = b'pr'
76 MERGE_RECORD_RESOLVED_PATH = b'pr'
80 MERGE_RECORD_DRIVER_RESOLVED = b'd'
81 # represents that the file was automatically merged in favor
77 # represents that the file was automatically merged in favor
82 # of other version. This info is used on commit.
78 # of other version. This info is used on commit.
83 # This is now deprecated and commit related information is now
79 # This is now deprecated and commit related information is now
84 # stored in RECORD_FILE_VALUES
80 # stored in RECORD_FILE_VALUES
85 MERGE_RECORD_MERGED_OTHER = b'o'
81 MERGE_RECORD_MERGED_OTHER = b'o'
86
82
87 #####
83 #####
88 # top level record which stores other unknown records. Multiple of these can
84 # top level record which stores other unknown records. Multiple of these can
89 # exists
85 # exists
90 #####
86 #####
91 RECORD_OVERRIDE = b't'
87 RECORD_OVERRIDE = b't'
92
88
93 #####
89 #####
94 # possible states which a merge driver can have. These are stored inside a
95 # RECORD_MERGE_DRIVER_STATE entry
96 #####
97 MERGE_DRIVER_STATE_UNMARKED = b'u'
98 MERGE_DRIVER_STATE_MARKED = b'm'
99 MERGE_DRIVER_STATE_SUCCESS = b's'
100
101 #####
102 # legacy records which are no longer used but kept to prevent breaking BC
90 # legacy records which are no longer used but kept to prevent breaking BC
103 #####
91 #####
104 # This record was release in 5.4 and usage was removed in 5.5
92 # This record was release in 5.4 and usage was removed in 5.5
105 LEGACY_RECORD_RESOLVED_OTHER = b'R'
93 LEGACY_RECORD_RESOLVED_OTHER = b'R'
94 # This record was release in 3.7 and usage was removed in 5.6
95 LEGACY_RECORD_DRIVER_RESOLVED = b'd'
96 # This record was release in 3.7 and usage was removed in 5.6
97 LEGACY_MERGE_DRIVER_STATE = b'm'
98 # This record was release in 3.7 and usage was removed in 5.6
99 LEGACY_MERGE_DRIVER_MERGE = b'D'
106
100
107
101
108 ACTION_FORGET = b'f'
102 ACTION_FORGET = b'f'
109 ACTION_REMOVE = b'r'
103 ACTION_REMOVE = b'r'
110 ACTION_ADD = b'a'
104 ACTION_ADD = b'a'
111 ACTION_GET = b'g'
105 ACTION_GET = b'g'
112 ACTION_PATH_CONFLICT = b'p'
106 ACTION_PATH_CONFLICT = b'p'
113 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
107 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
114 ACTION_ADD_MODIFIED = b'am'
108 ACTION_ADD_MODIFIED = b'am'
115 ACTION_CREATED = b'c'
109 ACTION_CREATED = b'c'
116 ACTION_DELETED_CHANGED = b'dc'
110 ACTION_DELETED_CHANGED = b'dc'
117 ACTION_CHANGED_DELETED = b'cd'
111 ACTION_CHANGED_DELETED = b'cd'
118 ACTION_MERGE = b'm'
112 ACTION_MERGE = b'm'
119 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
113 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
120 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
114 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
121 ACTION_KEEP = b'k'
115 ACTION_KEEP = b'k'
122 # the file was absent on local side before merge and we should
116 # the file was absent on local side before merge and we should
123 # keep it absent (absent means file not present, it can be a result
117 # keep it absent (absent means file not present, it can be a result
124 # of file deletion, rename etc.)
118 # of file deletion, rename etc.)
125 ACTION_KEEP_ABSENT = b'ka'
119 ACTION_KEEP_ABSENT = b'ka'
126 ACTION_EXEC = b'e'
120 ACTION_EXEC = b'e'
127 ACTION_CREATED_MERGE = b'cm'
121 ACTION_CREATED_MERGE = b'cm'
128
122
129
123
130 class _mergestate_base(object):
124 class _mergestate_base(object):
131 '''track 3-way merge state of individual files
125 '''track 3-way merge state of individual files
132
126
133 The merge state is stored on disk when needed. Two files are used: one with
127 The merge state is stored on disk when needed. Two files are used: one with
134 an old format (version 1), and one with a new format (version 2). Version 2
128 an old format (version 1), and one with a new format (version 2). Version 2
135 stores a superset of the data in version 1, including new kinds of records
129 stores a superset of the data in version 1, including new kinds of records
136 in the future. For more about the new format, see the documentation for
130 in the future. For more about the new format, see the documentation for
137 `_readrecordsv2`.
131 `_readrecordsv2`.
138
132
139 Each record can contain arbitrary content, and has an associated type. This
133 Each record can contain arbitrary content, and has an associated type. This
140 `type` should be a letter. If `type` is uppercase, the record is mandatory:
134 `type` should be a letter. If `type` is uppercase, the record is mandatory:
141 versions of Mercurial that don't support it should abort. If `type` is
135 versions of Mercurial that don't support it should abort. If `type` is
142 lowercase, the record can be safely ignored.
136 lowercase, the record can be safely ignored.
143
137
144 Currently known records:
138 Currently known records:
145
139
146 L: the node of the "local" part of the merge (hexified version)
140 L: the node of the "local" part of the merge (hexified version)
147 O: the node of the "other" part of the merge (hexified version)
141 O: the node of the "other" part of the merge (hexified version)
148 F: a file to be merged entry
142 F: a file to be merged entry
149 C: a change/delete or delete/change conflict
143 C: a change/delete or delete/change conflict
150 D: a file that the external merge driver will merge internally
151 (experimental)
152 P: a path conflict (file vs directory)
144 P: a path conflict (file vs directory)
153 m: the external merge driver defined for this merge plus its run state
154 (experimental)
155 f: a (filename, dictionary) tuple of optional values for a given file
145 f: a (filename, dictionary) tuple of optional values for a given file
156 l: the labels for the parts of the merge.
146 l: the labels for the parts of the merge.
157
147
158 Merge driver run states (experimental):
159 u: driver-resolved files unmarked -- needs to be run next time we're about
160 to resolve or commit
161 m: driver-resolved files marked -- only needs to be run before commit
162 s: success/skipped -- does not need to be run any more
163
164 Merge record states (stored in self._state, indexed by filename):
148 Merge record states (stored in self._state, indexed by filename):
165 u: unresolved conflict
149 u: unresolved conflict
166 r: resolved conflict
150 r: resolved conflict
167 pu: unresolved path conflict (file conflicts with directory)
151 pu: unresolved path conflict (file conflicts with directory)
168 pr: resolved path conflict
152 pr: resolved path conflict
169 d: driver-resolved conflict
170
153
171 The resolve command transitions between 'u' and 'r' for conflicts and
154 The resolve command transitions between 'u' and 'r' for conflicts and
172 'pu' and 'pr' for path conflicts.
155 'pu' and 'pr' for path conflicts.
173 '''
156 '''
174
157
175 def __init__(self, repo):
158 def __init__(self, repo):
176 """Initialize the merge state.
159 """Initialize the merge state.
177
160
178 Do not use this directly! Instead call read() or clean()."""
161 Do not use this directly! Instead call read() or clean()."""
179 self._repo = repo
162 self._repo = repo
180 self._state = {}
163 self._state = {}
181 self._stateextras = collections.defaultdict(dict)
164 self._stateextras = collections.defaultdict(dict)
182 self._local = None
165 self._local = None
183 self._other = None
166 self._other = None
184 self._labels = None
167 self._labels = None
185 self._readmergedriver = None
186 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
187 # contains a mapping of form:
168 # contains a mapping of form:
188 # {filename : (merge_return_value, action_to_be_performed}
169 # {filename : (merge_return_value, action_to_be_performed}
189 # these are results of re-running merge process
170 # these are results of re-running merge process
190 # this dict is used to perform actions on dirstate caused by re-running
171 # this dict is used to perform actions on dirstate caused by re-running
191 # the merge
172 # the merge
192 self._results = {}
173 self._results = {}
193 self._dirty = False
174 self._dirty = False
194
175
195 def reset(self):
176 def reset(self):
196 pass
177 pass
197
178
198 def start(self, node, other, labels=None):
179 def start(self, node, other, labels=None):
199 self._local = node
180 self._local = node
200 self._other = other
181 self._other = other
201 self._labels = labels
182 self._labels = labels
202 if self.mergedriver:
203 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
204
205 @util.propertycache
206 def mergedriver(self):
207 # protect against the following:
208 # - A configures a malicious merge driver in their hgrc, then
209 # pauses the merge
210 # - A edits their hgrc to remove references to the merge driver
211 # - A gives a copy of their entire repo, including .hg, to B
212 # - B inspects .hgrc and finds it to be clean
213 # - B then continues the merge and the malicious merge driver
214 # gets invoked
215 configmergedriver = self._repo.ui.config(
216 b'experimental', b'mergedriver'
217 )
218 if (
219 self._readmergedriver is not None
220 and self._readmergedriver != configmergedriver
221 ):
222 raise error.ConfigError(
223 _(b"merge driver changed since merge started"),
224 hint=_(b"revert merge driver change or abort merge"),
225 )
226
227 return configmergedriver
228
183
229 @util.propertycache
184 @util.propertycache
230 def local(self):
185 def local(self):
231 if self._local is None:
186 if self._local is None:
232 msg = b"local accessed but self._local isn't set"
187 msg = b"local accessed but self._local isn't set"
233 raise error.ProgrammingError(msg)
188 raise error.ProgrammingError(msg)
234 return self._local
189 return self._local
235
190
236 @util.propertycache
191 @util.propertycache
237 def localctx(self):
192 def localctx(self):
238 return self._repo[self.local]
193 return self._repo[self.local]
239
194
240 @util.propertycache
195 @util.propertycache
241 def other(self):
196 def other(self):
242 if self._other is None:
197 if self._other is None:
243 msg = b"other accessed but self._other isn't set"
198 msg = b"other accessed but self._other isn't set"
244 raise error.ProgrammingError(msg)
199 raise error.ProgrammingError(msg)
245 return self._other
200 return self._other
246
201
247 @util.propertycache
202 @util.propertycache
248 def otherctx(self):
203 def otherctx(self):
249 return self._repo[self.other]
204 return self._repo[self.other]
250
205
251 def active(self):
206 def active(self):
252 """Whether mergestate is active.
207 """Whether mergestate is active.
253
208
254 Returns True if there appears to be mergestate. This is a rough proxy
209 Returns True if there appears to be mergestate. This is a rough proxy
255 for "is a merge in progress."
210 for "is a merge in progress."
256 """
211 """
257 return bool(self._local) or bool(self._state)
212 return bool(self._local) or bool(self._state)
258
213
259 def commit(self):
214 def commit(self):
260 """Write current state on disk (if necessary)"""
215 """Write current state on disk (if necessary)"""
261
216
262 @staticmethod
217 @staticmethod
263 def getlocalkey(path):
218 def getlocalkey(path):
264 """hash the path of a local file context for storage in the .hg/merge
219 """hash the path of a local file context for storage in the .hg/merge
265 directory."""
220 directory."""
266
221
267 return hex(hashutil.sha1(path).digest())
222 return hex(hashutil.sha1(path).digest())
268
223
269 def _make_backup(self, fctx, localkey):
224 def _make_backup(self, fctx, localkey):
270 raise NotImplementedError()
225 raise NotImplementedError()
271
226
272 def _restore_backup(self, fctx, localkey, flags):
227 def _restore_backup(self, fctx, localkey, flags):
273 raise NotImplementedError()
228 raise NotImplementedError()
274
229
275 def add(self, fcl, fco, fca, fd):
230 def add(self, fcl, fco, fca, fd):
276 """add a new (potentially?) conflicting file the merge state
231 """add a new (potentially?) conflicting file the merge state
277 fcl: file context for local,
232 fcl: file context for local,
278 fco: file context for remote,
233 fco: file context for remote,
279 fca: file context for ancestors,
234 fca: file context for ancestors,
280 fd: file path of the resulting merge.
235 fd: file path of the resulting merge.
281
236
282 note: also write the local version to the `.hg/merge` directory.
237 note: also write the local version to the `.hg/merge` directory.
283 """
238 """
284 if fcl.isabsent():
239 if fcl.isabsent():
285 localkey = nullhex
240 localkey = nullhex
286 else:
241 else:
287 localkey = mergestate.getlocalkey(fcl.path())
242 localkey = mergestate.getlocalkey(fcl.path())
288 self._make_backup(fcl, localkey)
243 self._make_backup(fcl, localkey)
289 self._state[fd] = [
244 self._state[fd] = [
290 MERGE_RECORD_UNRESOLVED,
245 MERGE_RECORD_UNRESOLVED,
291 localkey,
246 localkey,
292 fcl.path(),
247 fcl.path(),
293 fca.path(),
248 fca.path(),
294 hex(fca.filenode()),
249 hex(fca.filenode()),
295 fco.path(),
250 fco.path(),
296 hex(fco.filenode()),
251 hex(fco.filenode()),
297 fcl.flags(),
252 fcl.flags(),
298 ]
253 ]
299 self._stateextras[fd] = {b'ancestorlinknode': hex(fca.node())}
254 self._stateextras[fd] = {b'ancestorlinknode': hex(fca.node())}
300 self._dirty = True
255 self._dirty = True
301
256
302 def addpathconflict(self, path, frename, forigin):
257 def addpathconflict(self, path, frename, forigin):
303 """add a new conflicting path to the merge state
258 """add a new conflicting path to the merge state
304 path: the path that conflicts
259 path: the path that conflicts
305 frename: the filename the conflicting file was renamed to
260 frename: the filename the conflicting file was renamed to
306 forigin: origin of the file ('l' or 'r' for local/remote)
261 forigin: origin of the file ('l' or 'r' for local/remote)
307 """
262 """
308 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
263 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
309 self._dirty = True
264 self._dirty = True
310
265
311 def addcommitinfo(self, path, data):
266 def addcommitinfo(self, path, data):
312 """ stores information which is required at commit
267 """ stores information which is required at commit
313 into _stateextras """
268 into _stateextras """
314 self._stateextras[path].update(data)
269 self._stateextras[path].update(data)
315 self._dirty = True
270 self._dirty = True
316
271
317 def __contains__(self, dfile):
272 def __contains__(self, dfile):
318 return dfile in self._state
273 return dfile in self._state
319
274
320 def __getitem__(self, dfile):
275 def __getitem__(self, dfile):
321 return self._state[dfile][0]
276 return self._state[dfile][0]
322
277
323 def __iter__(self):
278 def __iter__(self):
324 return iter(sorted(self._state))
279 return iter(sorted(self._state))
325
280
326 def files(self):
281 def files(self):
327 return self._state.keys()
282 return self._state.keys()
328
283
329 def mark(self, dfile, state):
284 def mark(self, dfile, state):
330 self._state[dfile][0] = state
285 self._state[dfile][0] = state
331 self._dirty = True
286 self._dirty = True
332
287
333 def mdstate(self):
334 return self._mdstate
335
336 def unresolved(self):
288 def unresolved(self):
337 """Obtain the paths of unresolved files."""
289 """Obtain the paths of unresolved files."""
338
290
339 for f, entry in pycompat.iteritems(self._state):
291 for f, entry in pycompat.iteritems(self._state):
340 if entry[0] in (
292 if entry[0] in (
341 MERGE_RECORD_UNRESOLVED,
293 MERGE_RECORD_UNRESOLVED,
342 MERGE_RECORD_UNRESOLVED_PATH,
294 MERGE_RECORD_UNRESOLVED_PATH,
343 ):
295 ):
344 yield f
296 yield f
345
297
346 def driverresolved(self):
347 """Obtain the paths of driver-resolved files."""
348
349 for f, entry in self._state.items():
350 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
351 yield f
352
353 def extras(self, filename):
298 def extras(self, filename):
354 return self._stateextras[filename]
299 return self._stateextras[filename]
355
300
356 def _resolve(self, preresolve, dfile, wctx):
301 def _resolve(self, preresolve, dfile, wctx):
357 """rerun merge process for file path `dfile`.
302 """rerun merge process for file path `dfile`.
358 Returns whether the merge was completed and the return value of merge
303 Returns whether the merge was completed and the return value of merge
359 obtained from filemerge._filemerge().
304 obtained from filemerge._filemerge().
360 """
305 """
361 if self[dfile] in (MERGE_RECORD_RESOLVED, MERGE_RECORD_DRIVER_RESOLVED):
306 if self[dfile] in (
307 MERGE_RECORD_RESOLVED,
308 LEGACY_RECORD_DRIVER_RESOLVED,
309 ):
362 return True, 0
310 return True, 0
363 stateentry = self._state[dfile]
311 stateentry = self._state[dfile]
364 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
312 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
365 octx = self._repo[self._other]
313 octx = self._repo[self._other]
366 extras = self.extras(dfile)
314 extras = self.extras(dfile)
367 anccommitnode = extras.get(b'ancestorlinknode')
315 anccommitnode = extras.get(b'ancestorlinknode')
368 if anccommitnode:
316 if anccommitnode:
369 actx = self._repo[anccommitnode]
317 actx = self._repo[anccommitnode]
370 else:
318 else:
371 actx = None
319 actx = None
372 fcd = _filectxorabsent(localkey, wctx, dfile)
320 fcd = _filectxorabsent(localkey, wctx, dfile)
373 fco = _filectxorabsent(onode, octx, ofile)
321 fco = _filectxorabsent(onode, octx, ofile)
374 # TODO: move this to filectxorabsent
322 # TODO: move this to filectxorabsent
375 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
323 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
376 # "premerge" x flags
324 # "premerge" x flags
377 flo = fco.flags()
325 flo = fco.flags()
378 fla = fca.flags()
326 fla = fca.flags()
379 if b'x' in flags + flo + fla and b'l' not in flags + flo + fla:
327 if b'x' in flags + flo + fla and b'l' not in flags + flo + fla:
380 if fca.node() == nullid and flags != flo:
328 if fca.node() == nullid and flags != flo:
381 if preresolve:
329 if preresolve:
382 self._repo.ui.warn(
330 self._repo.ui.warn(
383 _(
331 _(
384 b'warning: cannot merge flags for %s '
332 b'warning: cannot merge flags for %s '
385 b'without common ancestor - keeping local flags\n'
333 b'without common ancestor - keeping local flags\n'
386 )
334 )
387 % afile
335 % afile
388 )
336 )
389 elif flags == fla:
337 elif flags == fla:
390 flags = flo
338 flags = flo
391 if preresolve:
339 if preresolve:
392 # restore local
340 # restore local
393 if localkey != nullhex:
341 if localkey != nullhex:
394 self._restore_backup(wctx[dfile], localkey, flags)
342 self._restore_backup(wctx[dfile], localkey, flags)
395 else:
343 else:
396 wctx[dfile].remove(ignoremissing=True)
344 wctx[dfile].remove(ignoremissing=True)
397 complete, merge_ret, deleted = filemerge.premerge(
345 complete, merge_ret, deleted = filemerge.premerge(
398 self._repo,
346 self._repo,
399 wctx,
347 wctx,
400 self._local,
348 self._local,
401 lfile,
349 lfile,
402 fcd,
350 fcd,
403 fco,
351 fco,
404 fca,
352 fca,
405 labels=self._labels,
353 labels=self._labels,
406 )
354 )
407 else:
355 else:
408 complete, merge_ret, deleted = filemerge.filemerge(
356 complete, merge_ret, deleted = filemerge.filemerge(
409 self._repo,
357 self._repo,
410 wctx,
358 wctx,
411 self._local,
359 self._local,
412 lfile,
360 lfile,
413 fcd,
361 fcd,
414 fco,
362 fco,
415 fca,
363 fca,
416 labels=self._labels,
364 labels=self._labels,
417 )
365 )
418 if merge_ret is None:
366 if merge_ret is None:
419 # If return value of merge is None, then there are no real conflict
367 # If return value of merge is None, then there are no real conflict
420 del self._state[dfile]
368 del self._state[dfile]
421 self._stateextras.pop(dfile, None)
369 self._stateextras.pop(dfile, None)
422 self._dirty = True
370 self._dirty = True
423 elif not merge_ret:
371 elif not merge_ret:
424 self.mark(dfile, MERGE_RECORD_RESOLVED)
372 self.mark(dfile, MERGE_RECORD_RESOLVED)
425
373
426 if complete:
374 if complete:
427 action = None
375 action = None
428 if deleted:
376 if deleted:
429 if fcd.isabsent():
377 if fcd.isabsent():
430 # dc: local picked. Need to drop if present, which may
378 # dc: local picked. Need to drop if present, which may
431 # happen on re-resolves.
379 # happen on re-resolves.
432 action = ACTION_FORGET
380 action = ACTION_FORGET
433 else:
381 else:
434 # cd: remote picked (or otherwise deleted)
382 # cd: remote picked (or otherwise deleted)
435 action = ACTION_REMOVE
383 action = ACTION_REMOVE
436 else:
384 else:
437 if fcd.isabsent(): # dc: remote picked
385 if fcd.isabsent(): # dc: remote picked
438 action = ACTION_GET
386 action = ACTION_GET
439 elif fco.isabsent(): # cd: local picked
387 elif fco.isabsent(): # cd: local picked
440 if dfile in self.localctx:
388 if dfile in self.localctx:
441 action = ACTION_ADD_MODIFIED
389 action = ACTION_ADD_MODIFIED
442 else:
390 else:
443 action = ACTION_ADD
391 action = ACTION_ADD
444 # else: regular merges (no action necessary)
392 # else: regular merges (no action necessary)
445 self._results[dfile] = merge_ret, action
393 self._results[dfile] = merge_ret, action
446
394
447 return complete, merge_ret
395 return complete, merge_ret
448
396
449 def preresolve(self, dfile, wctx):
397 def preresolve(self, dfile, wctx):
450 """run premerge process for dfile
398 """run premerge process for dfile
451
399
452 Returns whether the merge is complete, and the exit code."""
400 Returns whether the merge is complete, and the exit code."""
453 return self._resolve(True, dfile, wctx)
401 return self._resolve(True, dfile, wctx)
454
402
455 def resolve(self, dfile, wctx):
403 def resolve(self, dfile, wctx):
456 """run merge process (assuming premerge was run) for dfile
404 """run merge process (assuming premerge was run) for dfile
457
405
458 Returns the exit code of the merge."""
406 Returns the exit code of the merge."""
459 return self._resolve(False, dfile, wctx)[1]
407 return self._resolve(False, dfile, wctx)[1]
460
408
461 def counts(self):
409 def counts(self):
462 """return counts for updated, merged and removed files in this
410 """return counts for updated, merged and removed files in this
463 session"""
411 session"""
464 updated, merged, removed = 0, 0, 0
412 updated, merged, removed = 0, 0, 0
465 for r, action in pycompat.itervalues(self._results):
413 for r, action in pycompat.itervalues(self._results):
466 if r is None:
414 if r is None:
467 updated += 1
415 updated += 1
468 elif r == 0:
416 elif r == 0:
469 if action == ACTION_REMOVE:
417 if action == ACTION_REMOVE:
470 removed += 1
418 removed += 1
471 else:
419 else:
472 merged += 1
420 merged += 1
473 return updated, merged, removed
421 return updated, merged, removed
474
422
475 def unresolvedcount(self):
423 def unresolvedcount(self):
476 """get unresolved count for this merge (persistent)"""
424 """get unresolved count for this merge (persistent)"""
477 return len(list(self.unresolved()))
425 return len(list(self.unresolved()))
478
426
479 def actions(self):
427 def actions(self):
480 """return lists of actions to perform on the dirstate"""
428 """return lists of actions to perform on the dirstate"""
481 actions = {
429 actions = {
482 ACTION_REMOVE: [],
430 ACTION_REMOVE: [],
483 ACTION_FORGET: [],
431 ACTION_FORGET: [],
484 ACTION_ADD: [],
432 ACTION_ADD: [],
485 ACTION_ADD_MODIFIED: [],
433 ACTION_ADD_MODIFIED: [],
486 ACTION_GET: [],
434 ACTION_GET: [],
487 }
435 }
488 for f, (r, action) in pycompat.iteritems(self._results):
436 for f, (r, action) in pycompat.iteritems(self._results):
489 if action is not None:
437 if action is not None:
490 actions[action].append((f, None, b"merge result"))
438 actions[action].append((f, None, b"merge result"))
491 return actions
439 return actions
492
440
493 def queueremove(self, f):
494 """queues a file to be removed from the dirstate
495
496 Meant for use by custom merge drivers."""
497 self._results[f] = 0, ACTION_REMOVE
498
499 def queueadd(self, f):
500 """queues a file to be added to the dirstate
501
502 Meant for use by custom merge drivers."""
503 self._results[f] = 0, ACTION_ADD
504
505 def queueget(self, f):
506 """queues a file to be marked modified in the dirstate
507
508 Meant for use by custom merge drivers."""
509 self._results[f] = 0, ACTION_GET
510
511
441
512 class mergestate(_mergestate_base):
442 class mergestate(_mergestate_base):
513
443
514 statepathv1 = b'merge/state'
444 statepathv1 = b'merge/state'
515 statepathv2 = b'merge/state2'
445 statepathv2 = b'merge/state2'
516
446
517 @staticmethod
447 @staticmethod
518 def clean(repo):
448 def clean(repo):
519 """Initialize a brand new merge state, removing any existing state on
449 """Initialize a brand new merge state, removing any existing state on
520 disk."""
450 disk."""
521 ms = mergestate(repo)
451 ms = mergestate(repo)
522 ms.reset()
452 ms.reset()
523 return ms
453 return ms
524
454
525 @staticmethod
455 @staticmethod
526 def read(repo):
456 def read(repo):
527 """Initialize the merge state, reading it from disk."""
457 """Initialize the merge state, reading it from disk."""
528 ms = mergestate(repo)
458 ms = mergestate(repo)
529 ms._read()
459 ms._read()
530 return ms
460 return ms
531
461
532 def _read(self):
462 def _read(self):
533 """Analyse each record content to restore a serialized state from disk
463 """Analyse each record content to restore a serialized state from disk
534
464
535 This function process "record" entry produced by the de-serialization
465 This function process "record" entry produced by the de-serialization
536 of on disk file.
466 of on disk file.
537 """
467 """
538 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
539 unsupported = set()
468 unsupported = set()
540 records = self._readrecords()
469 records = self._readrecords()
541 for rtype, record in records:
470 for rtype, record in records:
542 if rtype == RECORD_LOCAL:
471 if rtype == RECORD_LOCAL:
543 self._local = bin(record)
472 self._local = bin(record)
544 elif rtype == RECORD_OTHER:
473 elif rtype == RECORD_OTHER:
545 self._other = bin(record)
474 self._other = bin(record)
546 elif rtype == RECORD_MERGE_DRIVER_STATE:
475 elif rtype == LEGACY_MERGE_DRIVER_STATE:
547 bits = record.split(b'\0', 1)
476 pass
548 mdstate = bits[1]
549 if len(mdstate) != 1 or mdstate not in (
550 MERGE_DRIVER_STATE_UNMARKED,
551 MERGE_DRIVER_STATE_MARKED,
552 MERGE_DRIVER_STATE_SUCCESS,
553 ):
554 # the merge driver should be idempotent, so just rerun it
555 mdstate = MERGE_DRIVER_STATE_UNMARKED
556
557 self._readmergedriver = bits[0]
558 self._mdstate = mdstate
559 elif rtype in (
477 elif rtype in (
560 RECORD_MERGED,
478 RECORD_MERGED,
561 RECORD_CHANGEDELETE_CONFLICT,
479 RECORD_CHANGEDELETE_CONFLICT,
562 RECORD_PATH_CONFLICT,
480 RECORD_PATH_CONFLICT,
563 RECORD_MERGE_DRIVER_MERGE,
481 LEGACY_MERGE_DRIVER_MERGE,
564 LEGACY_RECORD_RESOLVED_OTHER,
482 LEGACY_RECORD_RESOLVED_OTHER,
565 ):
483 ):
566 bits = record.split(b'\0')
484 bits = record.split(b'\0')
567 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
485 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
568 # and we now store related information in _stateextras, so
486 # and we now store related information in _stateextras, so
569 # lets write to _stateextras directly
487 # lets write to _stateextras directly
570 if bits[1] == MERGE_RECORD_MERGED_OTHER:
488 if bits[1] == MERGE_RECORD_MERGED_OTHER:
571 self._stateextras[bits[0]][b'filenode-source'] = b'other'
489 self._stateextras[bits[0]][b'filenode-source'] = b'other'
572 else:
490 else:
573 self._state[bits[0]] = bits[1:]
491 self._state[bits[0]] = bits[1:]
574 elif rtype == RECORD_FILE_VALUES:
492 elif rtype == RECORD_FILE_VALUES:
575 filename, rawextras = record.split(b'\0', 1)
493 filename, rawextras = record.split(b'\0', 1)
576 extraparts = rawextras.split(b'\0')
494 extraparts = rawextras.split(b'\0')
577 extras = {}
495 extras = {}
578 i = 0
496 i = 0
579 while i < len(extraparts):
497 while i < len(extraparts):
580 extras[extraparts[i]] = extraparts[i + 1]
498 extras[extraparts[i]] = extraparts[i + 1]
581 i += 2
499 i += 2
582
500
583 self._stateextras[filename] = extras
501 self._stateextras[filename] = extras
584 elif rtype == RECORD_LABELS:
502 elif rtype == RECORD_LABELS:
585 labels = record.split(b'\0', 2)
503 labels = record.split(b'\0', 2)
586 self._labels = [l for l in labels if len(l) > 0]
504 self._labels = [l for l in labels if len(l) > 0]
587 elif not rtype.islower():
505 elif not rtype.islower():
588 unsupported.add(rtype)
506 unsupported.add(rtype)
589
507
590 if unsupported:
508 if unsupported:
591 raise error.UnsupportedMergeRecords(unsupported)
509 raise error.UnsupportedMergeRecords(unsupported)
592
510
593 def _readrecords(self):
511 def _readrecords(self):
594 """Read merge state from disk and return a list of record (TYPE, data)
512 """Read merge state from disk and return a list of record (TYPE, data)
595
513
596 We read data from both v1 and v2 files and decide which one to use.
514 We read data from both v1 and v2 files and decide which one to use.
597
515
598 V1 has been used by version prior to 2.9.1 and contains less data than
516 V1 has been used by version prior to 2.9.1 and contains less data than
599 v2. We read both versions and check if no data in v2 contradicts
517 v2. We read both versions and check if no data in v2 contradicts
600 v1. If there is not contradiction we can safely assume that both v1
518 v1. If there is not contradiction we can safely assume that both v1
601 and v2 were written at the same time and use the extract data in v2. If
519 and v2 were written at the same time and use the extract data in v2. If
602 there is contradiction we ignore v2 content as we assume an old version
520 there is contradiction we ignore v2 content as we assume an old version
603 of Mercurial has overwritten the mergestate file and left an old v2
521 of Mercurial has overwritten the mergestate file and left an old v2
604 file around.
522 file around.
605
523
606 returns list of record [(TYPE, data), ...]"""
524 returns list of record [(TYPE, data), ...]"""
607 v1records = self._readrecordsv1()
525 v1records = self._readrecordsv1()
608 v2records = self._readrecordsv2()
526 v2records = self._readrecordsv2()
609 if self._v1v2match(v1records, v2records):
527 if self._v1v2match(v1records, v2records):
610 return v2records
528 return v2records
611 else:
529 else:
612 # v1 file is newer than v2 file, use it
530 # v1 file is newer than v2 file, use it
613 # we have to infer the "other" changeset of the merge
531 # we have to infer the "other" changeset of the merge
614 # we cannot do better than that with v1 of the format
532 # we cannot do better than that with v1 of the format
615 mctx = self._repo[None].parents()[-1]
533 mctx = self._repo[None].parents()[-1]
616 v1records.append((RECORD_OTHER, mctx.hex()))
534 v1records.append((RECORD_OTHER, mctx.hex()))
617 # add place holder "other" file node information
535 # add place holder "other" file node information
618 # nobody is using it yet so we do no need to fetch the data
536 # nobody is using it yet so we do no need to fetch the data
619 # if mctx was wrong `mctx[bits[-2]]` may fails.
537 # if mctx was wrong `mctx[bits[-2]]` may fails.
620 for idx, r in enumerate(v1records):
538 for idx, r in enumerate(v1records):
621 if r[0] == RECORD_MERGED:
539 if r[0] == RECORD_MERGED:
622 bits = r[1].split(b'\0')
540 bits = r[1].split(b'\0')
623 bits.insert(-2, b'')
541 bits.insert(-2, b'')
624 v1records[idx] = (r[0], b'\0'.join(bits))
542 v1records[idx] = (r[0], b'\0'.join(bits))
625 return v1records
543 return v1records
626
544
627 def _v1v2match(self, v1records, v2records):
545 def _v1v2match(self, v1records, v2records):
628 oldv2 = set() # old format version of v2 record
546 oldv2 = set() # old format version of v2 record
629 for rec in v2records:
547 for rec in v2records:
630 if rec[0] == RECORD_LOCAL:
548 if rec[0] == RECORD_LOCAL:
631 oldv2.add(rec)
549 oldv2.add(rec)
632 elif rec[0] == RECORD_MERGED:
550 elif rec[0] == RECORD_MERGED:
633 # drop the onode data (not contained in v1)
551 # drop the onode data (not contained in v1)
634 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
552 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
635 for rec in v1records:
553 for rec in v1records:
636 if rec not in oldv2:
554 if rec not in oldv2:
637 return False
555 return False
638 else:
556 else:
639 return True
557 return True
640
558
641 def _readrecordsv1(self):
559 def _readrecordsv1(self):
642 """read on disk merge state for version 1 file
560 """read on disk merge state for version 1 file
643
561
644 returns list of record [(TYPE, data), ...]
562 returns list of record [(TYPE, data), ...]
645
563
646 Note: the "F" data from this file are one entry short
564 Note: the "F" data from this file are one entry short
647 (no "other file node" entry)
565 (no "other file node" entry)
648 """
566 """
649 records = []
567 records = []
650 try:
568 try:
651 f = self._repo.vfs(self.statepathv1)
569 f = self._repo.vfs(self.statepathv1)
652 for i, l in enumerate(f):
570 for i, l in enumerate(f):
653 if i == 0:
571 if i == 0:
654 records.append((RECORD_LOCAL, l[:-1]))
572 records.append((RECORD_LOCAL, l[:-1]))
655 else:
573 else:
656 records.append((RECORD_MERGED, l[:-1]))
574 records.append((RECORD_MERGED, l[:-1]))
657 f.close()
575 f.close()
658 except IOError as err:
576 except IOError as err:
659 if err.errno != errno.ENOENT:
577 if err.errno != errno.ENOENT:
660 raise
578 raise
661 return records
579 return records
662
580
663 def _readrecordsv2(self):
581 def _readrecordsv2(self):
664 """read on disk merge state for version 2 file
582 """read on disk merge state for version 2 file
665
583
666 This format is a list of arbitrary records of the form:
584 This format is a list of arbitrary records of the form:
667
585
668 [type][length][content]
586 [type][length][content]
669
587
670 `type` is a single character, `length` is a 4 byte integer, and
588 `type` is a single character, `length` is a 4 byte integer, and
671 `content` is an arbitrary byte sequence of length `length`.
589 `content` is an arbitrary byte sequence of length `length`.
672
590
673 Mercurial versions prior to 3.7 have a bug where if there are
591 Mercurial versions prior to 3.7 have a bug where if there are
674 unsupported mandatory merge records, attempting to clear out the merge
592 unsupported mandatory merge records, attempting to clear out the merge
675 state with hg update --clean or similar aborts. The 't' record type
593 state with hg update --clean or similar aborts. The 't' record type
676 works around that by writing out what those versions treat as an
594 works around that by writing out what those versions treat as an
677 advisory record, but later versions interpret as special: the first
595 advisory record, but later versions interpret as special: the first
678 character is the 'real' record type and everything onwards is the data.
596 character is the 'real' record type and everything onwards is the data.
679
597
680 Returns list of records [(TYPE, data), ...]."""
598 Returns list of records [(TYPE, data), ...]."""
681 records = []
599 records = []
682 try:
600 try:
683 f = self._repo.vfs(self.statepathv2)
601 f = self._repo.vfs(self.statepathv2)
684 data = f.read()
602 data = f.read()
685 off = 0
603 off = 0
686 end = len(data)
604 end = len(data)
687 while off < end:
605 while off < end:
688 rtype = data[off : off + 1]
606 rtype = data[off : off + 1]
689 off += 1
607 off += 1
690 length = _unpack(b'>I', data[off : (off + 4)])[0]
608 length = _unpack(b'>I', data[off : (off + 4)])[0]
691 off += 4
609 off += 4
692 record = data[off : (off + length)]
610 record = data[off : (off + length)]
693 off += length
611 off += length
694 if rtype == RECORD_OVERRIDE:
612 if rtype == RECORD_OVERRIDE:
695 rtype, record = record[0:1], record[1:]
613 rtype, record = record[0:1], record[1:]
696 records.append((rtype, record))
614 records.append((rtype, record))
697 f.close()
615 f.close()
698 except IOError as err:
616 except IOError as err:
699 if err.errno != errno.ENOENT:
617 if err.errno != errno.ENOENT:
700 raise
618 raise
701 return records
619 return records
702
620
703 def commit(self):
621 def commit(self):
704 if self._dirty:
622 if self._dirty:
705 records = self._makerecords()
623 records = self._makerecords()
706 self._writerecords(records)
624 self._writerecords(records)
707 self._dirty = False
625 self._dirty = False
708
626
709 def _makerecords(self):
627 def _makerecords(self):
710 records = []
628 records = []
711 records.append((RECORD_LOCAL, hex(self._local)))
629 records.append((RECORD_LOCAL, hex(self._local)))
712 records.append((RECORD_OTHER, hex(self._other)))
630 records.append((RECORD_OTHER, hex(self._other)))
713 if self.mergedriver:
714 records.append(
715 (
716 RECORD_MERGE_DRIVER_STATE,
717 b'\0'.join([self.mergedriver, self._mdstate]),
718 )
719 )
720 # Write out state items. In all cases, the value of the state map entry
631 # Write out state items. In all cases, the value of the state map entry
721 # is written as the contents of the record. The record type depends on
632 # is written as the contents of the record. The record type depends on
722 # the type of state that is stored, and capital-letter records are used
633 # the type of state that is stored, and capital-letter records are used
723 # to prevent older versions of Mercurial that do not support the feature
634 # to prevent older versions of Mercurial that do not support the feature
724 # from loading them.
635 # from loading them.
725 for filename, v in pycompat.iteritems(self._state):
636 for filename, v in pycompat.iteritems(self._state):
726 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
637 if v[0] in (
727 # Driver-resolved merge. These are stored in 'D' records.
728 records.append(
729 (RECORD_MERGE_DRIVER_MERGE, b'\0'.join([filename] + v))
730 )
731 elif v[0] in (
732 MERGE_RECORD_UNRESOLVED_PATH,
638 MERGE_RECORD_UNRESOLVED_PATH,
733 MERGE_RECORD_RESOLVED_PATH,
639 MERGE_RECORD_RESOLVED_PATH,
734 ):
640 ):
735 # Path conflicts. These are stored in 'P' records. The current
641 # Path conflicts. These are stored in 'P' records. The current
736 # resolution state ('pu' or 'pr') is stored within the record.
642 # resolution state ('pu' or 'pr') is stored within the record.
737 records.append(
643 records.append(
738 (RECORD_PATH_CONFLICT, b'\0'.join([filename] + v))
644 (RECORD_PATH_CONFLICT, b'\0'.join([filename] + v))
739 )
645 )
740 elif v[1] == nullhex or v[6] == nullhex:
646 elif v[1] == nullhex or v[6] == nullhex:
741 # Change/Delete or Delete/Change conflicts. These are stored in
647 # Change/Delete or Delete/Change conflicts. These are stored in
742 # 'C' records. v[1] is the local file, and is nullhex when the
648 # 'C' records. v[1] is the local file, and is nullhex when the
743 # file is deleted locally ('dc'). v[6] is the remote file, and
649 # file is deleted locally ('dc'). v[6] is the remote file, and
744 # is nullhex when the file is deleted remotely ('cd').
650 # is nullhex when the file is deleted remotely ('cd').
745 records.append(
651 records.append(
746 (RECORD_CHANGEDELETE_CONFLICT, b'\0'.join([filename] + v))
652 (RECORD_CHANGEDELETE_CONFLICT, b'\0'.join([filename] + v))
747 )
653 )
748 else:
654 else:
749 # Normal files. These are stored in 'F' records.
655 # Normal files. These are stored in 'F' records.
750 records.append((RECORD_MERGED, b'\0'.join([filename] + v)))
656 records.append((RECORD_MERGED, b'\0'.join([filename] + v)))
751 for filename, extras in sorted(pycompat.iteritems(self._stateextras)):
657 for filename, extras in sorted(pycompat.iteritems(self._stateextras)):
752 rawextras = b'\0'.join(
658 rawextras = b'\0'.join(
753 b'%s\0%s' % (k, v) for k, v in pycompat.iteritems(extras)
659 b'%s\0%s' % (k, v) for k, v in pycompat.iteritems(extras)
754 )
660 )
755 records.append(
661 records.append(
756 (RECORD_FILE_VALUES, b'%s\0%s' % (filename, rawextras))
662 (RECORD_FILE_VALUES, b'%s\0%s' % (filename, rawextras))
757 )
663 )
758 if self._labels is not None:
664 if self._labels is not None:
759 labels = b'\0'.join(self._labels)
665 labels = b'\0'.join(self._labels)
760 records.append((RECORD_LABELS, labels))
666 records.append((RECORD_LABELS, labels))
761 return records
667 return records
762
668
763 def _writerecords(self, records):
669 def _writerecords(self, records):
764 """Write current state on disk (both v1 and v2)"""
670 """Write current state on disk (both v1 and v2)"""
765 self._writerecordsv1(records)
671 self._writerecordsv1(records)
766 self._writerecordsv2(records)
672 self._writerecordsv2(records)
767
673
768 def _writerecordsv1(self, records):
674 def _writerecordsv1(self, records):
769 """Write current state on disk in a version 1 file"""
675 """Write current state on disk in a version 1 file"""
770 f = self._repo.vfs(self.statepathv1, b'wb')
676 f = self._repo.vfs(self.statepathv1, b'wb')
771 irecords = iter(records)
677 irecords = iter(records)
772 lrecords = next(irecords)
678 lrecords = next(irecords)
773 assert lrecords[0] == RECORD_LOCAL
679 assert lrecords[0] == RECORD_LOCAL
774 f.write(hex(self._local) + b'\n')
680 f.write(hex(self._local) + b'\n')
775 for rtype, data in irecords:
681 for rtype, data in irecords:
776 if rtype == RECORD_MERGED:
682 if rtype == RECORD_MERGED:
777 f.write(b'%s\n' % _droponode(data))
683 f.write(b'%s\n' % _droponode(data))
778 f.close()
684 f.close()
779
685
780 def _writerecordsv2(self, records):
686 def _writerecordsv2(self, records):
781 """Write current state on disk in a version 2 file
687 """Write current state on disk in a version 2 file
782
688
783 See the docstring for _readrecordsv2 for why we use 't'."""
689 See the docstring for _readrecordsv2 for why we use 't'."""
784 # these are the records that all version 2 clients can read
690 # these are the records that all version 2 clients can read
785 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
691 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
786 f = self._repo.vfs(self.statepathv2, b'wb')
692 f = self._repo.vfs(self.statepathv2, b'wb')
787 for key, data in records:
693 for key, data in records:
788 assert len(key) == 1
694 assert len(key) == 1
789 if key not in allowlist:
695 if key not in allowlist:
790 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
696 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
791 format = b'>sI%is' % len(data)
697 format = b'>sI%is' % len(data)
792 f.write(_pack(format, key, len(data), data))
698 f.write(_pack(format, key, len(data), data))
793 f.close()
699 f.close()
794
700
795 def _make_backup(self, fctx, localkey):
701 def _make_backup(self, fctx, localkey):
796 self._repo.vfs.write(b'merge/' + localkey, fctx.data())
702 self._repo.vfs.write(b'merge/' + localkey, fctx.data())
797
703
798 def _restore_backup(self, fctx, localkey, flags):
704 def _restore_backup(self, fctx, localkey, flags):
799 with self._repo.vfs(b'merge/' + localkey) as f:
705 with self._repo.vfs(b'merge/' + localkey) as f:
800 fctx.write(f.read(), flags)
706 fctx.write(f.read(), flags)
801
707
802 def reset(self):
708 def reset(self):
803 shutil.rmtree(self._repo.vfs.join(b'merge'), True)
709 shutil.rmtree(self._repo.vfs.join(b'merge'), True)
804
710
805
711
806 class memmergestate(_mergestate_base):
712 class memmergestate(_mergestate_base):
807 def __init__(self, repo):
713 def __init__(self, repo):
808 super(memmergestate, self).__init__(repo)
714 super(memmergestate, self).__init__(repo)
809 self._backups = {}
715 self._backups = {}
810
716
811 def _make_backup(self, fctx, localkey):
717 def _make_backup(self, fctx, localkey):
812 self._backups[localkey] = fctx.data()
718 self._backups[localkey] = fctx.data()
813
719
814 def _restore_backup(self, fctx, localkey, flags):
720 def _restore_backup(self, fctx, localkey, flags):
815 fctx.write(self._backups[localkey], flags)
721 fctx.write(self._backups[localkey], flags)
816
722
817 @util.propertycache
818 def mergedriver(self):
819 configmergedriver = self._repo.ui.config(
820 b'experimental', b'mergedriver'
821 )
822 if configmergedriver:
823 raise error.InMemoryMergeConflictsError(
824 b"in-memory merge does not support mergedriver"
825 )
826 return None
827
828
723
829 def recordupdates(repo, actions, branchmerge, getfiledata):
724 def recordupdates(repo, actions, branchmerge, getfiledata):
830 """record merge actions to the dirstate"""
725 """record merge actions to the dirstate"""
831 # remove (must come first)
726 # remove (must come first)
832 for f, args, msg in actions.get(ACTION_REMOVE, []):
727 for f, args, msg in actions.get(ACTION_REMOVE, []):
833 if branchmerge:
728 if branchmerge:
834 repo.dirstate.remove(f)
729 repo.dirstate.remove(f)
835 else:
730 else:
836 repo.dirstate.drop(f)
731 repo.dirstate.drop(f)
837
732
838 # forget (must come first)
733 # forget (must come first)
839 for f, args, msg in actions.get(ACTION_FORGET, []):
734 for f, args, msg in actions.get(ACTION_FORGET, []):
840 repo.dirstate.drop(f)
735 repo.dirstate.drop(f)
841
736
842 # resolve path conflicts
737 # resolve path conflicts
843 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
738 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
844 (f0, origf0) = args
739 (f0, origf0) = args
845 repo.dirstate.add(f)
740 repo.dirstate.add(f)
846 repo.dirstate.copy(origf0, f)
741 repo.dirstate.copy(origf0, f)
847 if f0 == origf0:
742 if f0 == origf0:
848 repo.dirstate.remove(f0)
743 repo.dirstate.remove(f0)
849 else:
744 else:
850 repo.dirstate.drop(f0)
745 repo.dirstate.drop(f0)
851
746
852 # re-add
747 # re-add
853 for f, args, msg in actions.get(ACTION_ADD, []):
748 for f, args, msg in actions.get(ACTION_ADD, []):
854 repo.dirstate.add(f)
749 repo.dirstate.add(f)
855
750
856 # re-add/mark as modified
751 # re-add/mark as modified
857 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
752 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
858 if branchmerge:
753 if branchmerge:
859 repo.dirstate.normallookup(f)
754 repo.dirstate.normallookup(f)
860 else:
755 else:
861 repo.dirstate.add(f)
756 repo.dirstate.add(f)
862
757
863 # exec change
758 # exec change
864 for f, args, msg in actions.get(ACTION_EXEC, []):
759 for f, args, msg in actions.get(ACTION_EXEC, []):
865 repo.dirstate.normallookup(f)
760 repo.dirstate.normallookup(f)
866
761
867 # keep
762 # keep
868 for f, args, msg in actions.get(ACTION_KEEP, []):
763 for f, args, msg in actions.get(ACTION_KEEP, []):
869 pass
764 pass
870
765
871 # keep deleted
766 # keep deleted
872 for f, args, msg in actions.get(ACTION_KEEP_ABSENT, []):
767 for f, args, msg in actions.get(ACTION_KEEP_ABSENT, []):
873 pass
768 pass
874
769
875 # get
770 # get
876 for f, args, msg in actions.get(ACTION_GET, []):
771 for f, args, msg in actions.get(ACTION_GET, []):
877 if branchmerge:
772 if branchmerge:
878 repo.dirstate.otherparent(f)
773 repo.dirstate.otherparent(f)
879 else:
774 else:
880 parentfiledata = getfiledata[f] if getfiledata else None
775 parentfiledata = getfiledata[f] if getfiledata else None
881 repo.dirstate.normal(f, parentfiledata=parentfiledata)
776 repo.dirstate.normal(f, parentfiledata=parentfiledata)
882
777
883 # merge
778 # merge
884 for f, args, msg in actions.get(ACTION_MERGE, []):
779 for f, args, msg in actions.get(ACTION_MERGE, []):
885 f1, f2, fa, move, anc = args
780 f1, f2, fa, move, anc = args
886 if branchmerge:
781 if branchmerge:
887 # We've done a branch merge, mark this file as merged
782 # We've done a branch merge, mark this file as merged
888 # so that we properly record the merger later
783 # so that we properly record the merger later
889 repo.dirstate.merge(f)
784 repo.dirstate.merge(f)
890 if f1 != f2: # copy/rename
785 if f1 != f2: # copy/rename
891 if move:
786 if move:
892 repo.dirstate.remove(f1)
787 repo.dirstate.remove(f1)
893 if f1 != f:
788 if f1 != f:
894 repo.dirstate.copy(f1, f)
789 repo.dirstate.copy(f1, f)
895 else:
790 else:
896 repo.dirstate.copy(f2, f)
791 repo.dirstate.copy(f2, f)
897 else:
792 else:
898 # We've update-merged a locally modified file, so
793 # We've update-merged a locally modified file, so
899 # we set the dirstate to emulate a normal checkout
794 # we set the dirstate to emulate a normal checkout
900 # of that file some time in the past. Thus our
795 # of that file some time in the past. Thus our
901 # merge will appear as a normal local file
796 # merge will appear as a normal local file
902 # modification.
797 # modification.
903 if f2 == f: # file not locally copied/moved
798 if f2 == f: # file not locally copied/moved
904 repo.dirstate.normallookup(f)
799 repo.dirstate.normallookup(f)
905 if move:
800 if move:
906 repo.dirstate.drop(f1)
801 repo.dirstate.drop(f1)
907
802
908 # directory rename, move local
803 # directory rename, move local
909 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
804 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
910 f0, flag = args
805 f0, flag = args
911 if branchmerge:
806 if branchmerge:
912 repo.dirstate.add(f)
807 repo.dirstate.add(f)
913 repo.dirstate.remove(f0)
808 repo.dirstate.remove(f0)
914 repo.dirstate.copy(f0, f)
809 repo.dirstate.copy(f0, f)
915 else:
810 else:
916 repo.dirstate.normal(f)
811 repo.dirstate.normal(f)
917 repo.dirstate.drop(f0)
812 repo.dirstate.drop(f0)
918
813
919 # directory rename, get
814 # directory rename, get
920 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
815 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
921 f0, flag = args
816 f0, flag = args
922 if branchmerge:
817 if branchmerge:
923 repo.dirstate.add(f)
818 repo.dirstate.add(f)
924 repo.dirstate.copy(f0, f)
819 repo.dirstate.copy(f0, f)
925 else:
820 else:
926 repo.dirstate.normal(f)
821 repo.dirstate.normal(f)
@@ -1,24 +1,19 b''
1 # mergeutil.py - help for merge processing in mercurial
1 # mergeutil.py - help for merge processing in 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 from .i18n import _
10 from .i18n import _
11
11
12 from . import error
12 from . import error
13
13
14
14
15 def checkunresolved(ms):
15 def checkunresolved(ms):
16 if list(ms.unresolved()):
16 if list(ms.unresolved()):
17 raise error.Abort(
17 raise error.Abort(
18 _(b"unresolved merge conflicts (see 'hg help resolve')")
18 _(b"unresolved merge conflicts (see 'hg help resolve')")
19 )
19 )
20 if ms.mdstate() != b's' or list(ms.driverresolved()):
21 raise error.Abort(
22 _(b'driver-resolved merge conflicts'),
23 hint=_(b'run "hg resolve --all" to resolve'),
24 )
@@ -1,796 +1,738 b''
1 test that a commit clears the merge state.
1 test that a commit clears the merge state.
2
2
3 $ hg init repo
3 $ hg init repo
4 $ cd repo
4 $ cd repo
5
5
6 $ echo foo > file1
6 $ echo foo > file1
7 $ echo foo > file2
7 $ echo foo > file2
8 $ hg commit -Am 'add files'
8 $ hg commit -Am 'add files'
9 adding file1
9 adding file1
10 adding file2
10 adding file2
11
11
12 $ echo bar >> file1
12 $ echo bar >> file1
13 $ echo bar >> file2
13 $ echo bar >> file2
14 $ hg commit -Am 'append bar to files'
14 $ hg commit -Am 'append bar to files'
15
15
16 create a second head with conflicting edits
16 create a second head with conflicting edits
17
17
18 $ hg up -C 0
18 $ hg up -C 0
19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 $ echo baz >> file1
20 $ echo baz >> file1
21 $ echo baz >> file2
21 $ echo baz >> file2
22 $ hg commit -Am 'append baz to files'
22 $ hg commit -Am 'append baz to files'
23 created new head
23 created new head
24
24
25 create a third head with no conflicting edits
25 create a third head with no conflicting edits
26 $ hg up -qC 0
26 $ hg up -qC 0
27 $ echo foo > file3
27 $ echo foo > file3
28 $ hg commit -Am 'add non-conflicting file'
28 $ hg commit -Am 'add non-conflicting file'
29 adding file3
29 adding file3
30 created new head
30 created new head
31
31
32 failing merge
32 failing merge
33
33
34 $ hg up -qC 2
34 $ hg up -qC 2
35 $ hg merge --tool=internal:fail 1
35 $ hg merge --tool=internal:fail 1
36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
37 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
37 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
38 [1]
38 [1]
39
39
40 resolve -l should contain unresolved entries
40 resolve -l should contain unresolved entries
41
41
42 $ hg resolve -l
42 $ hg resolve -l
43 U file1
43 U file1
44 U file2
44 U file2
45
45
46 $ hg resolve -l --no-status
46 $ hg resolve -l --no-status
47 file1
47 file1
48 file2
48 file2
49
49
50 resolving an unknown path should emit a warning, but not for -l
50 resolving an unknown path should emit a warning, but not for -l
51
51
52 $ hg resolve -m does-not-exist
52 $ hg resolve -m does-not-exist
53 arguments do not match paths that need resolving
53 arguments do not match paths that need resolving
54 $ hg resolve -l does-not-exist
54 $ hg resolve -l does-not-exist
55
55
56 tell users how they could have used resolve
56 tell users how they could have used resolve
57
57
58 $ mkdir nested
58 $ mkdir nested
59 $ cd nested
59 $ cd nested
60 $ hg resolve -m file1
60 $ hg resolve -m file1
61 arguments do not match paths that need resolving
61 arguments do not match paths that need resolving
62 (try: hg resolve -m path:file1)
62 (try: hg resolve -m path:file1)
63 $ hg resolve -m file1 filez
63 $ hg resolve -m file1 filez
64 arguments do not match paths that need resolving
64 arguments do not match paths that need resolving
65 (try: hg resolve -m path:file1 path:filez)
65 (try: hg resolve -m path:file1 path:filez)
66 $ hg resolve -m path:file1 path:filez
66 $ hg resolve -m path:file1 path:filez
67 $ hg resolve -l
67 $ hg resolve -l
68 R file1
68 R file1
69 U file2
69 U file2
70 $ hg resolve -l --config ui.relative-paths=yes
70 $ hg resolve -l --config ui.relative-paths=yes
71 R ../file1
71 R ../file1
72 U ../file2
72 U ../file2
73 $ hg resolve --re-merge filez file2
73 $ hg resolve --re-merge filez file2
74 arguments do not match paths that need resolving
74 arguments do not match paths that need resolving
75 (try: hg resolve --re-merge path:filez path:file2)
75 (try: hg resolve --re-merge path:filez path:file2)
76 $ hg resolve -m filez file2
76 $ hg resolve -m filez file2
77 arguments do not match paths that need resolving
77 arguments do not match paths that need resolving
78 (try: hg resolve -m path:filez path:file2)
78 (try: hg resolve -m path:filez path:file2)
79 $ hg resolve -m path:filez path:file2
79 $ hg resolve -m path:filez path:file2
80 (no more unresolved files)
80 (no more unresolved files)
81 $ hg resolve -l
81 $ hg resolve -l
82 R file1
82 R file1
83 R file2
83 R file2
84
84
85 cleanup
85 cleanup
86 $ hg resolve -u
86 $ hg resolve -u
87 $ cd ..
87 $ cd ..
88 $ rmdir nested
88 $ rmdir nested
89
89
90 don't allow marking or unmarking driver-resolved files
91
92 $ cat > $TESTTMP/markdriver.py << EOF
93 > '''mark and unmark files as driver-resolved'''
94 > from mercurial import (
95 > mergestate,
96 > pycompat,
97 > registrar,
98 > scmutil,
99 > )
100 > cmdtable = {}
101 > command = registrar.command(cmdtable)
102 > @command(b'markdriver',
103 > [(b'u', b'unmark', None, b'')],
104 > b'FILE...')
105 > def markdriver(ui, repo, *pats, **opts):
106 > wlock = repo.wlock()
107 > opts = pycompat.byteskwargs(opts)
108 > try:
109 > ms = mergestate.mergestate.read(repo)
110 > m = scmutil.match(repo[None], pats, opts)
111 > for f in ms:
112 > if not m(f):
113 > continue
114 > if not opts[b'unmark']:
115 > ms.mark(f, b'd')
116 > else:
117 > ms.mark(f, b'u')
118 > ms.commit()
119 > finally:
120 > wlock.release()
121 > EOF
122 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver file1
123 $ hg resolve --list
124 D file1
125 U file2
126 $ hg resolve --mark file1
127 not marking file1 as it is driver-resolved
128 this should not print out file1
129 $ hg resolve --mark --all
130 (no more unresolved files -- run "hg resolve --all" to conclude)
131 $ hg resolve --mark 'glob:file*'
132 (no more unresolved files -- run "hg resolve --all" to conclude)
133 $ hg resolve --list
134 D file1
135 R file2
136 $ hg resolve --unmark file1
137 not unmarking file1 as it is driver-resolved
138 (no more unresolved files -- run "hg resolve --all" to conclude)
139 $ hg resolve --unmark --all
140 $ hg resolve --list
141 D file1
142 U file2
143 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver --unmark file1
144 $ hg resolve --list
145 U file1
146 U file2
147
148 resolve the failure
90 resolve the failure
149
91
150 $ echo resolved > file1
92 $ echo resolved > file1
151 $ hg resolve -m file1
93 $ hg resolve -m file1
152
94
153 resolve -l should show resolved file as resolved
95 resolve -l should show resolved file as resolved
154
96
155 $ hg resolve -l
97 $ hg resolve -l
156 R file1
98 R file1
157 U file2
99 U file2
158
100
159 $ hg resolve -l -Tjson
101 $ hg resolve -l -Tjson
160 [
102 [
161 {
103 {
162 "mergestatus": "R",
104 "mergestatus": "R",
163 "path": "file1"
105 "path": "file1"
164 },
106 },
165 {
107 {
166 "mergestatus": "U",
108 "mergestatus": "U",
167 "path": "file2"
109 "path": "file2"
168 }
110 }
169 ]
111 ]
170
112
171 $ hg resolve -l -T '{path} {mergestatus} {status} {p1rev} {p2rev}\n'
113 $ hg resolve -l -T '{path} {mergestatus} {status} {p1rev} {p2rev}\n'
172 file1 R M 2 1
114 file1 R M 2 1
173 file2 U M 2 1
115 file2 U M 2 1
174
116
175 resolve -m without paths should mark all resolved
117 resolve -m without paths should mark all resolved
176
118
177 $ hg resolve -m
119 $ hg resolve -m
178 (no more unresolved files)
120 (no more unresolved files)
179 $ hg commit -m 'resolved'
121 $ hg commit -m 'resolved'
180
122
181 resolve -l should be empty after commit
123 resolve -l should be empty after commit
182
124
183 $ hg resolve -l
125 $ hg resolve -l
184
126
185 $ hg resolve -l -Tjson
127 $ hg resolve -l -Tjson
186 [
128 [
187 ]
129 ]
188
130
189 resolve --all should abort when no merge in progress
131 resolve --all should abort when no merge in progress
190
132
191 $ hg resolve --all
133 $ hg resolve --all
192 abort: resolve command not applicable when not merging
134 abort: resolve command not applicable when not merging
193 [255]
135 [255]
194
136
195 resolve -m should abort when no merge in progress
137 resolve -m should abort when no merge in progress
196
138
197 $ hg resolve -m
139 $ hg resolve -m
198 abort: resolve command not applicable when not merging
140 abort: resolve command not applicable when not merging
199 [255]
141 [255]
200
142
201 can not update or merge when there are unresolved conflicts
143 can not update or merge when there are unresolved conflicts
202
144
203 $ hg up -qC 0
145 $ hg up -qC 0
204 $ echo quux >> file1
146 $ echo quux >> file1
205 $ hg up 1
147 $ hg up 1
206 merging file1
148 merging file1
207 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
149 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
208 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
150 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
209 use 'hg resolve' to retry unresolved file merges
151 use 'hg resolve' to retry unresolved file merges
210 [1]
152 [1]
211 $ hg up 0
153 $ hg up 0
212 abort: outstanding merge conflicts
154 abort: outstanding merge conflicts
213 (use 'hg resolve' to resolve)
155 (use 'hg resolve' to resolve)
214 [255]
156 [255]
215 $ hg merge 2
157 $ hg merge 2
216 abort: outstanding merge conflicts
158 abort: outstanding merge conflicts
217 (use 'hg resolve' to resolve)
159 (use 'hg resolve' to resolve)
218 [255]
160 [255]
219 $ hg merge --force 2
161 $ hg merge --force 2
220 abort: outstanding merge conflicts
162 abort: outstanding merge conflicts
221 (use 'hg resolve' to resolve)
163 (use 'hg resolve' to resolve)
222 [255]
164 [255]
223
165
224 set up conflict-free merge
166 set up conflict-free merge
225
167
226 $ hg up -qC 3
168 $ hg up -qC 3
227 $ hg merge 1
169 $ hg merge 1
228 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 (branch merge, don't forget to commit)
171 (branch merge, don't forget to commit)
230
172
231 resolve --all should do nothing in merge without conflicts
173 resolve --all should do nothing in merge without conflicts
232 $ hg resolve --all
174 $ hg resolve --all
233 (no more unresolved files)
175 (no more unresolved files)
234
176
235 resolve -m should do nothing in merge without conflicts
177 resolve -m should do nothing in merge without conflicts
236
178
237 $ hg resolve -m
179 $ hg resolve -m
238 (no more unresolved files)
180 (no more unresolved files)
239
181
240 get back to conflicting state
182 get back to conflicting state
241
183
242 $ hg up -qC 2
184 $ hg up -qC 2
243 $ hg merge --tool=internal:fail 1
185 $ hg merge --tool=internal:fail 1
244 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
186 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
245 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
187 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
246 [1]
188 [1]
247
189
248 resolve without arguments should suggest --all
190 resolve without arguments should suggest --all
249 $ hg resolve
191 $ hg resolve
250 abort: no files or directories specified
192 abort: no files or directories specified
251 (use --all to re-merge all unresolved files)
193 (use --all to re-merge all unresolved files)
252 [255]
194 [255]
253
195
254 resolve --all should re-merge all unresolved files
196 resolve --all should re-merge all unresolved files
255 $ hg resolve --all
197 $ hg resolve --all
256 merging file1
198 merging file1
257 merging file2
199 merging file2
258 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
200 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
259 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
201 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
260 [1]
202 [1]
261 $ cat file1.orig
203 $ cat file1.orig
262 foo
204 foo
263 baz
205 baz
264 $ cat file2.orig
206 $ cat file2.orig
265 foo
207 foo
266 baz
208 baz
267
209
268 .orig files should exists where specified
210 .orig files should exists where specified
269 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
211 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
270 merging file1
212 merging file1
271 creating directory: $TESTTMP/repo/.hg/origbackups
213 creating directory: $TESTTMP/repo/.hg/origbackups
272 merging file2
214 merging file2
273 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
215 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
274 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
216 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
275 [1]
217 [1]
276 $ ls .hg/origbackups
218 $ ls .hg/origbackups
277 file1
219 file1
278 file2
220 file2
279 $ grep '<<<' file1 > /dev/null
221 $ grep '<<<' file1 > /dev/null
280 $ grep '<<<' file2 > /dev/null
222 $ grep '<<<' file2 > /dev/null
281
223
282 resolve <file> should re-merge file
224 resolve <file> should re-merge file
283 $ echo resolved > file1
225 $ echo resolved > file1
284 $ hg resolve -q file1
226 $ hg resolve -q file1
285 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
227 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
286 [1]
228 [1]
287 $ grep '<<<' file1 > /dev/null
229 $ grep '<<<' file1 > /dev/null
288
230
289 test .orig behavior with resolve
231 test .orig behavior with resolve
290
232
291 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
233 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
292 $TESTTMP/repo/file1.orig:
234 $TESTTMP/repo/file1.orig:
293 >>>
235 >>>
294 foo
236 foo
295 baz
237 baz
296 <<<
238 <<<
297
239
298 resolve <file> should do nothing if 'file' was marked resolved
240 resolve <file> should do nothing if 'file' was marked resolved
299 $ echo resolved > file1
241 $ echo resolved > file1
300 $ hg resolve -m file1
242 $ hg resolve -m file1
301 $ hg resolve -q file1
243 $ hg resolve -q file1
302 $ cat file1
244 $ cat file1
303 resolved
245 resolved
304
246
305 insert unsupported advisory merge record
247 insert unsupported advisory merge record
306
248
307 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
249 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
308 $ hg debugmergestate
250 $ hg debugmergestate
309 local (working copy): 57653b9f834a4493f7240b0681efcb9ae7cab745
251 local (working copy): 57653b9f834a4493f7240b0681efcb9ae7cab745
310 other (merge rev): dc77451844e37f03f5c559e3b8529b2b48d381d1
252 other (merge rev): dc77451844e37f03f5c559e3b8529b2b48d381d1
311 file: file1 (state "r")
253 file: file1 (state "r")
312 local path: file1 (hash 60b27f004e454aca81b0480209cce5081ec52390, flags "")
254 local path: file1 (hash 60b27f004e454aca81b0480209cce5081ec52390, flags "")
313 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
255 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
314 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
256 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
315 extra: ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac
257 extra: ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac
316 file: file2 (state "u")
258 file: file2 (state "u")
317 local path: file2 (hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523, flags "")
259 local path: file2 (hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523, flags "")
318 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
260 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
319 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
261 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
320 extra: ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac
262 extra: ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac
321 $ hg resolve -l
263 $ hg resolve -l
322 R file1
264 R file1
323 U file2
265 U file2
324
266
325 test json output
267 test json output
326
268
327 $ hg debugmergestate -T json
269 $ hg debugmergestate -T json
328 [
270 [
329 {
271 {
330 "commits": [{"label": "working copy", "name": "local", "node": "57653b9f834a4493f7240b0681efcb9ae7cab745"}, {"label": "merge rev", "name": "other", "node": "dc77451844e37f03f5c559e3b8529b2b48d381d1"}],
272 "commits": [{"label": "working copy", "name": "local", "node": "57653b9f834a4493f7240b0681efcb9ae7cab745"}, {"label": "merge rev", "name": "other", "node": "dc77451844e37f03f5c559e3b8529b2b48d381d1"}],
331 "extras": [],
273 "extras": [],
332 "files": [{"ancestor_node": "2ed2a3912a0b24502043eae84ee4b279c18b90dd", "ancestor_path": "file1", "extras": [{"key": "ancestorlinknode", "value": "99726c03216e233810a2564cbc0adfe395007eac"}], "local_flags": "", "local_key": "60b27f004e454aca81b0480209cce5081ec52390", "local_path": "file1", "other_node": "6f4310b00b9a147241b071a60c28a650827fb03d", "other_path": "file1", "path": "file1", "state": "r"}, {"ancestor_node": "2ed2a3912a0b24502043eae84ee4b279c18b90dd", "ancestor_path": "file2", "extras": [{"key": "ancestorlinknode", "value": "99726c03216e233810a2564cbc0adfe395007eac"}], "local_flags": "", "local_key": "cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523", "local_path": "file2", "other_node": "6f4310b00b9a147241b071a60c28a650827fb03d", "other_path": "file2", "path": "file2", "state": "u"}]
274 "files": [{"ancestor_node": "2ed2a3912a0b24502043eae84ee4b279c18b90dd", "ancestor_path": "file1", "extras": [{"key": "ancestorlinknode", "value": "99726c03216e233810a2564cbc0adfe395007eac"}], "local_flags": "", "local_key": "60b27f004e454aca81b0480209cce5081ec52390", "local_path": "file1", "other_node": "6f4310b00b9a147241b071a60c28a650827fb03d", "other_path": "file1", "path": "file1", "state": "r"}, {"ancestor_node": "2ed2a3912a0b24502043eae84ee4b279c18b90dd", "ancestor_path": "file2", "extras": [{"key": "ancestorlinknode", "value": "99726c03216e233810a2564cbc0adfe395007eac"}], "local_flags": "", "local_key": "cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523", "local_path": "file2", "other_node": "6f4310b00b9a147241b071a60c28a650827fb03d", "other_path": "file2", "path": "file2", "state": "u"}]
333 }
275 }
334 ]
276 ]
335
277
336
278
337 insert unsupported mandatory merge record
279 insert unsupported mandatory merge record
338
280
339 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
281 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
340 $ hg debugmergestate
282 $ hg debugmergestate
341 abort: unsupported merge state records: X
283 abort: unsupported merge state records: X
342 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
284 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
343 [255]
285 [255]
344 $ hg resolve -l
286 $ hg resolve -l
345 abort: unsupported merge state records: X
287 abort: unsupported merge state records: X
346 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
288 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
347 [255]
289 [255]
348 $ hg resolve -ma
290 $ hg resolve -ma
349 abort: unsupported merge state records: X
291 abort: unsupported merge state records: X
350 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
292 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
351 [255]
293 [255]
352 $ hg summary
294 $ hg summary
353 warning: merge state has unsupported record types: X
295 warning: merge state has unsupported record types: X
354 parent: 2:57653b9f834a
296 parent: 2:57653b9f834a
355 append baz to files
297 append baz to files
356 parent: 1:dc77451844e3
298 parent: 1:dc77451844e3
357 append bar to files
299 append bar to files
358 branch: default
300 branch: default
359 commit: 2 modified, 2 unknown (merge)
301 commit: 2 modified, 2 unknown (merge)
360 update: 2 new changesets (update)
302 update: 2 new changesets (update)
361 phases: 5 draft
303 phases: 5 draft
362
304
363 update --clean shouldn't abort on unsupported records
305 update --clean shouldn't abort on unsupported records
364
306
365 $ hg up -qC 1
307 $ hg up -qC 1
366 $ hg debugmergestate
308 $ hg debugmergestate
367 no merge state found
309 no merge state found
368
310
369 test crashed merge with empty mergestate
311 test crashed merge with empty mergestate
370
312
371 $ mkdir .hg/merge
313 $ mkdir .hg/merge
372 $ touch .hg/merge/state
314 $ touch .hg/merge/state
373
315
374 resolve -l should be empty
316 resolve -l should be empty
375
317
376 $ hg resolve -l
318 $ hg resolve -l
377
319
378 resolve -m can be configured to look for remaining conflict markers
320 resolve -m can be configured to look for remaining conflict markers
379 $ hg up -qC 2
321 $ hg up -qC 2
380 $ hg merge -q --tool=internal:merge 1
322 $ hg merge -q --tool=internal:merge 1
381 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
323 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
382 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
324 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
383 [1]
325 [1]
384 $ hg resolve -l
326 $ hg resolve -l
385 U file1
327 U file1
386 U file2
328 U file2
387 $ echo 'remove markers' > file1
329 $ echo 'remove markers' > file1
388 $ hg --config commands.resolve.mark-check=abort resolve -m
330 $ hg --config commands.resolve.mark-check=abort resolve -m
389 warning: the following files still have conflict markers:
331 warning: the following files still have conflict markers:
390 file2
332 file2
391 abort: conflict markers detected
333 abort: conflict markers detected
392 (use --all to mark anyway)
334 (use --all to mark anyway)
393 [255]
335 [255]
394 $ hg resolve -l
336 $ hg resolve -l
395 U file1
337 U file1
396 U file2
338 U file2
397 Try with --all from the hint
339 Try with --all from the hint
398 $ hg --config commands.resolve.mark-check=abort resolve -m --all
340 $ hg --config commands.resolve.mark-check=abort resolve -m --all
399 warning: the following files still have conflict markers:
341 warning: the following files still have conflict markers:
400 file2
342 file2
401 (no more unresolved files)
343 (no more unresolved files)
402 $ hg resolve -l
344 $ hg resolve -l
403 R file1
345 R file1
404 R file2
346 R file2
405 Test option value 'warn'
347 Test option value 'warn'
406 $ hg resolve --unmark
348 $ hg resolve --unmark
407 $ hg resolve -l
349 $ hg resolve -l
408 U file1
350 U file1
409 U file2
351 U file2
410 $ hg --config commands.resolve.mark-check=warn resolve -m
352 $ hg --config commands.resolve.mark-check=warn resolve -m
411 warning: the following files still have conflict markers:
353 warning: the following files still have conflict markers:
412 file2
354 file2
413 (no more unresolved files)
355 (no more unresolved files)
414 $ hg resolve -l
356 $ hg resolve -l
415 R file1
357 R file1
416 R file2
358 R file2
417 If the file is already marked as resolved, we don't warn about it
359 If the file is already marked as resolved, we don't warn about it
418 $ hg resolve --unmark file1
360 $ hg resolve --unmark file1
419 $ hg resolve -l
361 $ hg resolve -l
420 U file1
362 U file1
421 R file2
363 R file2
422 $ hg --config commands.resolve.mark-check=warn resolve -m
364 $ hg --config commands.resolve.mark-check=warn resolve -m
423 (no more unresolved files)
365 (no more unresolved files)
424 $ hg resolve -l
366 $ hg resolve -l
425 R file1
367 R file1
426 R file2
368 R file2
427 If the user passes an invalid value, we treat it as 'none'.
369 If the user passes an invalid value, we treat it as 'none'.
428 $ hg resolve --unmark
370 $ hg resolve --unmark
429 $ hg resolve -l
371 $ hg resolve -l
430 U file1
372 U file1
431 U file2
373 U file2
432 $ hg --config commands.resolve.mark-check=nope resolve -m
374 $ hg --config commands.resolve.mark-check=nope resolve -m
433 (no more unresolved files)
375 (no more unresolved files)
434 $ hg resolve -l
376 $ hg resolve -l
435 R file1
377 R file1
436 R file2
378 R file2
437 Test explicitly setting the option to 'none'
379 Test explicitly setting the option to 'none'
438 $ hg resolve --unmark
380 $ hg resolve --unmark
439 $ hg resolve -l
381 $ hg resolve -l
440 U file1
382 U file1
441 U file2
383 U file2
442 $ hg --config commands.resolve.mark-check=none resolve -m
384 $ hg --config commands.resolve.mark-check=none resolve -m
443 (no more unresolved files)
385 (no more unresolved files)
444 $ hg resolve -l
386 $ hg resolve -l
445 R file1
387 R file1
446 R file2
388 R file2
447 Test with marking an explicit file as resolved, this should not abort (since
389 Test with marking an explicit file as resolved, this should not abort (since
448 there's no --force flag, we have no way of combining --all with a filename)
390 there's no --force flag, we have no way of combining --all with a filename)
449 $ hg resolve --unmark
391 $ hg resolve --unmark
450 $ hg resolve -l
392 $ hg resolve -l
451 U file1
393 U file1
452 U file2
394 U file2
453 (This downgrades to a warning since an explicit file was specified).
395 (This downgrades to a warning since an explicit file was specified).
454 $ hg --config commands.resolve.mark-check=abort resolve -m file2
396 $ hg --config commands.resolve.mark-check=abort resolve -m file2
455 warning: the following files still have conflict markers:
397 warning: the following files still have conflict markers:
456 file2
398 file2
457 $ hg resolve -l
399 $ hg resolve -l
458 U file1
400 U file1
459 R file2
401 R file2
460 Testing the --re-merge flag
402 Testing the --re-merge flag
461 $ hg resolve --unmark file1
403 $ hg resolve --unmark file1
462 $ hg resolve -l
404 $ hg resolve -l
463 U file1
405 U file1
464 R file2
406 R file2
465 $ hg resolve --mark --re-merge
407 $ hg resolve --mark --re-merge
466 abort: too many actions specified
408 abort: too many actions specified
467 [255]
409 [255]
468 $ hg resolve --re-merge --all
410 $ hg resolve --re-merge --all
469 merging file1
411 merging file1
470 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
412 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
471 [1]
413 [1]
472 Explicit re-merge
414 Explicit re-merge
473 $ hg resolve --unmark file1
415 $ hg resolve --unmark file1
474 $ hg resolve --config commands.resolve.explicit-re-merge=1 --all
416 $ hg resolve --config commands.resolve.explicit-re-merge=1 --all
475 abort: no action specified
417 abort: no action specified
476 (use --mark, --unmark, --list or --re-merge)
418 (use --mark, --unmark, --list or --re-merge)
477 [255]
419 [255]
478 $ hg resolve --config commands.resolve.explicit-re-merge=1 --re-merge --all
420 $ hg resolve --config commands.resolve.explicit-re-merge=1 --re-merge --all
479 merging file1
421 merging file1
480 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
422 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
481 [1]
423 [1]
482
424
483 $ cd ..
425 $ cd ..
484
426
485 ======================================================
427 ======================================================
486 Test 'hg resolve' confirm config option functionality |
428 Test 'hg resolve' confirm config option functionality |
487 ======================================================
429 ======================================================
488 $ cat >> $HGRCPATH << EOF
430 $ cat >> $HGRCPATH << EOF
489 > [extensions]
431 > [extensions]
490 > rebase=
432 > rebase=
491 > EOF
433 > EOF
492
434
493 $ hg init repo2
435 $ hg init repo2
494 $ cd repo2
436 $ cd repo2
495
437
496 $ echo boss > boss
438 $ echo boss > boss
497 $ hg ci -Am "add boss"
439 $ hg ci -Am "add boss"
498 adding boss
440 adding boss
499
441
500 $ for emp in emp1 emp2 emp3; do echo work > $emp; done;
442 $ for emp in emp1 emp2 emp3; do echo work > $emp; done;
501 $ hg ci -Aqm "added emp1 emp2 emp3"
443 $ hg ci -Aqm "added emp1 emp2 emp3"
502
444
503 $ hg up 0
445 $ hg up 0
504 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
446 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
505
447
506 $ for emp in emp1 emp2 emp3; do echo nowork > $emp; done;
448 $ for emp in emp1 emp2 emp3; do echo nowork > $emp; done;
507 $ hg ci -Aqm "added lazy emp1 emp2 emp3"
449 $ hg ci -Aqm "added lazy emp1 emp2 emp3"
508
450
509 $ hg log -GT "{rev} {node|short} {firstline(desc)}\n"
451 $ hg log -GT "{rev} {node|short} {firstline(desc)}\n"
510 @ 2 0acfd4a49af0 added lazy emp1 emp2 emp3
452 @ 2 0acfd4a49af0 added lazy emp1 emp2 emp3
511 |
453 |
512 | o 1 f30f98a8181f added emp1 emp2 emp3
454 | o 1 f30f98a8181f added emp1 emp2 emp3
513 |/
455 |/
514 o 0 88660038d466 add boss
456 o 0 88660038d466 add boss
515
457
516 $ hg rebase -s 1 -d 2
458 $ hg rebase -s 1 -d 2
517 rebasing 1:f30f98a8181f "added emp1 emp2 emp3"
459 rebasing 1:f30f98a8181f "added emp1 emp2 emp3"
518 merging emp1
460 merging emp1
519 merging emp2
461 merging emp2
520 merging emp3
462 merging emp3
521 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
463 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
522 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
464 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
523 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
465 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
524 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
466 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
525 [1]
467 [1]
526
468
527 Test when commands.resolve.confirm config option is not set:
469 Test when commands.resolve.confirm config option is not set:
528 ===========================================================
470 ===========================================================
529 $ hg resolve --all
471 $ hg resolve --all
530 merging emp1
472 merging emp1
531 merging emp2
473 merging emp2
532 merging emp3
474 merging emp3
533 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
475 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
534 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
476 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
535 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
477 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
536 [1]
478 [1]
537
479
538 Test when config option is set:
480 Test when config option is set:
539 ==============================
481 ==============================
540 $ cat >> .hg/hgrc << EOF
482 $ cat >> .hg/hgrc << EOF
541 > [ui]
483 > [ui]
542 > interactive = True
484 > interactive = True
543 > [commands]
485 > [commands]
544 > resolve.confirm = True
486 > resolve.confirm = True
545 > EOF
487 > EOF
546
488
547 $ hg resolve
489 $ hg resolve
548 abort: no files or directories specified
490 abort: no files or directories specified
549 (use --all to re-merge all unresolved files)
491 (use --all to re-merge all unresolved files)
550 [255]
492 [255]
551 $ hg resolve --all << EOF
493 $ hg resolve --all << EOF
552 > n
494 > n
553 > EOF
495 > EOF
554 re-merge all unresolved files (yn)? n
496 re-merge all unresolved files (yn)? n
555 abort: user quit
497 abort: user quit
556 [255]
498 [255]
557
499
558 $ hg resolve --all << EOF
500 $ hg resolve --all << EOF
559 > y
501 > y
560 > EOF
502 > EOF
561 re-merge all unresolved files (yn)? y
503 re-merge all unresolved files (yn)? y
562 merging emp1
504 merging emp1
563 merging emp2
505 merging emp2
564 merging emp3
506 merging emp3
565 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
507 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
566 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
508 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
567 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
509 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
568 [1]
510 [1]
569
511
570 Test that commands.resolve.confirm respect --mark option (only when no patterns args are given):
512 Test that commands.resolve.confirm respect --mark option (only when no patterns args are given):
571 ===============================================================================================
513 ===============================================================================================
572
514
573 $ hg resolve -m emp1
515 $ hg resolve -m emp1
574 $ hg resolve -l
516 $ hg resolve -l
575 R emp1
517 R emp1
576 U emp2
518 U emp2
577 U emp3
519 U emp3
578
520
579 $ hg resolve -m << EOF
521 $ hg resolve -m << EOF
580 > n
522 > n
581 > EOF
523 > EOF
582 mark all unresolved files as resolved (yn)? n
524 mark all unresolved files as resolved (yn)? n
583 abort: user quit
525 abort: user quit
584 [255]
526 [255]
585
527
586 $ hg resolve -m << EOF
528 $ hg resolve -m << EOF
587 > y
529 > y
588 > EOF
530 > EOF
589 mark all unresolved files as resolved (yn)? y
531 mark all unresolved files as resolved (yn)? y
590 (no more unresolved files)
532 (no more unresolved files)
591 continue: hg rebase --continue
533 continue: hg rebase --continue
592 $ hg resolve -l
534 $ hg resolve -l
593 R emp1
535 R emp1
594 R emp2
536 R emp2
595 R emp3
537 R emp3
596
538
597 Test that commands.resolve.confirm respect --unmark option (only when no patterns args are given):
539 Test that commands.resolve.confirm respect --unmark option (only when no patterns args are given):
598 =================================================================================================
540 =================================================================================================
599
541
600 $ hg resolve -u emp1
542 $ hg resolve -u emp1
601
543
602 $ hg resolve -l
544 $ hg resolve -l
603 U emp1
545 U emp1
604 R emp2
546 R emp2
605 R emp3
547 R emp3
606
548
607 $ hg resolve -u << EOF
549 $ hg resolve -u << EOF
608 > n
550 > n
609 > EOF
551 > EOF
610 mark all resolved files as unresolved (yn)? n
552 mark all resolved files as unresolved (yn)? n
611 abort: user quit
553 abort: user quit
612 [255]
554 [255]
613
555
614 $ hg resolve -m << EOF
556 $ hg resolve -m << EOF
615 > y
557 > y
616 > EOF
558 > EOF
617 mark all unresolved files as resolved (yn)? y
559 mark all unresolved files as resolved (yn)? y
618 (no more unresolved files)
560 (no more unresolved files)
619 continue: hg rebase --continue
561 continue: hg rebase --continue
620
562
621 $ hg resolve -l
563 $ hg resolve -l
622 R emp1
564 R emp1
623 R emp2
565 R emp2
624 R emp3
566 R emp3
625
567
626 $ hg rebase --abort
568 $ hg rebase --abort
627 rebase aborted
569 rebase aborted
628
570
629 Done with commands.resolve.confirm tests:
571 Done with commands.resolve.confirm tests:
630 $ cd ..
572 $ cd ..
631
573
632 Test that commands.resolve.mark-check works even if there are deleted files:
574 Test that commands.resolve.mark-check works even if there are deleted files:
633 $ hg init resolve-deleted
575 $ hg init resolve-deleted
634 $ cd resolve-deleted
576 $ cd resolve-deleted
635 $ echo r0 > file1
577 $ echo r0 > file1
636 $ hg ci -qAm r0
578 $ hg ci -qAm r0
637 $ echo r1 > file1
579 $ echo r1 > file1
638 $ hg ci -qm r1
580 $ hg ci -qm r1
639 $ hg co -qr 0
581 $ hg co -qr 0
640 $ hg rm file1
582 $ hg rm file1
641 $ hg ci -qm "r2 (delete file1)"
583 $ hg ci -qm "r2 (delete file1)"
642
584
643 (At this point we have r0 creating file1, and sibling commits r1 and r2, which
585 (At this point we have r0 creating file1, and sibling commits r1 and r2, which
644 modify and delete file1, respectively)
586 modify and delete file1, respectively)
645
587
646 $ hg merge -r 1
588 $ hg merge -r 1
647 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
589 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
648 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
590 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
649 What do you want to do? u
591 What do you want to do? u
650 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
592 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
651 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
593 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
652 [1]
594 [1]
653 $ hg resolve --list
595 $ hg resolve --list
654 U file1
596 U file1
655 Because we left it as 'unresolved' the file should still exist.
597 Because we left it as 'unresolved' the file should still exist.
656 $ [ -f file1 ] || echo "File does not exist?"
598 $ [ -f file1 ] || echo "File does not exist?"
657 BC behavior: `hg resolve --mark` accepts that the file is still there, and
599 BC behavior: `hg resolve --mark` accepts that the file is still there, and
658 doesn't have a problem with this situation.
600 doesn't have a problem with this situation.
659 $ hg resolve --mark --config commands.resolve.mark-check=abort
601 $ hg resolve --mark --config commands.resolve.mark-check=abort
660 (no more unresolved files)
602 (no more unresolved files)
661 $ hg resolve --list
603 $ hg resolve --list
662 R file1
604 R file1
663 The file is still there:
605 The file is still there:
664 $ [ -f file1 ] || echo "File does not exist?"
606 $ [ -f file1 ] || echo "File does not exist?"
665 Let's check mark-check=warn:
607 Let's check mark-check=warn:
666 $ hg resolve --unmark file1
608 $ hg resolve --unmark file1
667 $ hg resolve --mark --config commands.resolve.mark-check=warn
609 $ hg resolve --mark --config commands.resolve.mark-check=warn
668 (no more unresolved files)
610 (no more unresolved files)
669 $ hg resolve --list
611 $ hg resolve --list
670 R file1
612 R file1
671 The file is still there:
613 The file is still there:
672 $ [ -f file1 ] || echo "File does not exist?"
614 $ [ -f file1 ] || echo "File does not exist?"
673 Let's resolve the issue by deleting the file via `hg resolve`
615 Let's resolve the issue by deleting the file via `hg resolve`
674 $ hg resolve --unmark file1
616 $ hg resolve --unmark file1
675 $ echo 'd' | hg resolve file1 --config ui.interactive=1
617 $ echo 'd' | hg resolve file1 --config ui.interactive=1
676 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
618 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
677 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
619 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
678 What do you want to do? d
620 What do you want to do? d
679 (no more unresolved files)
621 (no more unresolved files)
680 $ hg resolve --list
622 $ hg resolve --list
681 R file1
623 R file1
682 The file is deleted:
624 The file is deleted:
683 $ [ -f file1 ] && echo "File still exists?" || true
625 $ [ -f file1 ] && echo "File still exists?" || true
684 Doing `hg resolve --mark` doesn't break now that the file is missing:
626 Doing `hg resolve --mark` doesn't break now that the file is missing:
685 $ hg resolve --mark --config commands.resolve.mark-check=abort
627 $ hg resolve --mark --config commands.resolve.mark-check=abort
686 (no more unresolved files)
628 (no more unresolved files)
687 $ hg resolve --mark --config commands.resolve.mark-check=warn
629 $ hg resolve --mark --config commands.resolve.mark-check=warn
688 (no more unresolved files)
630 (no more unresolved files)
689 Resurrect the file, and delete it outside of hg:
631 Resurrect the file, and delete it outside of hg:
690 $ hg resolve --unmark file1
632 $ hg resolve --unmark file1
691 $ hg resolve file1
633 $ hg resolve file1
692 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
634 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
693 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
635 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
694 What do you want to do? u
636 What do you want to do? u
695 [1]
637 [1]
696 $ [ -f file1 ] || echo "File does not exist?"
638 $ [ -f file1 ] || echo "File does not exist?"
697 $ hg resolve --list
639 $ hg resolve --list
698 U file1
640 U file1
699 $ rm file1
641 $ rm file1
700 $ hg resolve --mark --config commands.resolve.mark-check=abort
642 $ hg resolve --mark --config commands.resolve.mark-check=abort
701 (no more unresolved files)
643 (no more unresolved files)
702 $ hg resolve --list
644 $ hg resolve --list
703 R file1
645 R file1
704 $ hg resolve --unmark file1
646 $ hg resolve --unmark file1
705 $ hg resolve file1
647 $ hg resolve file1
706 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
648 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
707 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
649 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
708 What do you want to do? u
650 What do you want to do? u
709 [1]
651 [1]
710 $ [ -f file1 ] || echo "File does not exist?"
652 $ [ -f file1 ] || echo "File does not exist?"
711 $ hg resolve --list
653 $ hg resolve --list
712 U file1
654 U file1
713 $ rm file1
655 $ rm file1
714 $ hg resolve --mark --config commands.resolve.mark-check=warn
656 $ hg resolve --mark --config commands.resolve.mark-check=warn
715 (no more unresolved files)
657 (no more unresolved files)
716 $ hg resolve --list
658 $ hg resolve --list
717 R file1
659 R file1
718
660
719
661
720 For completeness, let's try that in the opposite direction (merging r2 into r1,
662 For completeness, let's try that in the opposite direction (merging r2 into r1,
721 instead of r1 into r2):
663 instead of r1 into r2):
722 $ hg update -qCr 1
664 $ hg update -qCr 1
723 $ hg merge -r 2
665 $ hg merge -r 2
724 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
666 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
725 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
667 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
726 What do you want to do? u
668 What do you want to do? u
727 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
669 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
728 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
670 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
729 [1]
671 [1]
730 $ hg resolve --list
672 $ hg resolve --list
731 U file1
673 U file1
732 Because we left it as 'unresolved' the file should still exist.
674 Because we left it as 'unresolved' the file should still exist.
733 $ [ -f file1 ] || echo "File does not exist?"
675 $ [ -f file1 ] || echo "File does not exist?"
734 BC behavior: `hg resolve --mark` accepts that the file is still there, and
676 BC behavior: `hg resolve --mark` accepts that the file is still there, and
735 doesn't have a problem with this situation.
677 doesn't have a problem with this situation.
736 $ hg resolve --mark --config commands.resolve.mark-check=abort
678 $ hg resolve --mark --config commands.resolve.mark-check=abort
737 (no more unresolved files)
679 (no more unresolved files)
738 $ hg resolve --list
680 $ hg resolve --list
739 R file1
681 R file1
740 The file is still there:
682 The file is still there:
741 $ [ -f file1 ] || echo "File does not exist?"
683 $ [ -f file1 ] || echo "File does not exist?"
742 Let's check mark-check=warn:
684 Let's check mark-check=warn:
743 $ hg resolve --unmark file1
685 $ hg resolve --unmark file1
744 $ hg resolve --mark --config commands.resolve.mark-check=warn
686 $ hg resolve --mark --config commands.resolve.mark-check=warn
745 (no more unresolved files)
687 (no more unresolved files)
746 $ hg resolve --list
688 $ hg resolve --list
747 R file1
689 R file1
748 The file is still there:
690 The file is still there:
749 $ [ -f file1 ] || echo "File does not exist?"
691 $ [ -f file1 ] || echo "File does not exist?"
750 Let's resolve the issue by deleting the file via `hg resolve`
692 Let's resolve the issue by deleting the file via `hg resolve`
751 $ hg resolve --unmark file1
693 $ hg resolve --unmark file1
752 $ echo 'd' | hg resolve file1 --config ui.interactive=1
694 $ echo 'd' | hg resolve file1 --config ui.interactive=1
753 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
695 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
754 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
696 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
755 What do you want to do? d
697 What do you want to do? d
756 (no more unresolved files)
698 (no more unresolved files)
757 $ hg resolve --list
699 $ hg resolve --list
758 R file1
700 R file1
759 The file is deleted:
701 The file is deleted:
760 $ [ -f file1 ] && echo "File still exists?" || true
702 $ [ -f file1 ] && echo "File still exists?" || true
761 Doing `hg resolve --mark` doesn't break now that the file is missing:
703 Doing `hg resolve --mark` doesn't break now that the file is missing:
762 $ hg resolve --mark --config commands.resolve.mark-check=abort
704 $ hg resolve --mark --config commands.resolve.mark-check=abort
763 (no more unresolved files)
705 (no more unresolved files)
764 $ hg resolve --mark --config commands.resolve.mark-check=warn
706 $ hg resolve --mark --config commands.resolve.mark-check=warn
765 (no more unresolved files)
707 (no more unresolved files)
766 Resurrect the file, and delete it outside of hg:
708 Resurrect the file, and delete it outside of hg:
767 $ hg resolve --unmark file1
709 $ hg resolve --unmark file1
768 $ hg resolve file1
710 $ hg resolve file1
769 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
711 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
770 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
712 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
771 What do you want to do? u
713 What do you want to do? u
772 [1]
714 [1]
773 $ [ -f file1 ] || echo "File does not exist?"
715 $ [ -f file1 ] || echo "File does not exist?"
774 $ hg resolve --list
716 $ hg resolve --list
775 U file1
717 U file1
776 $ rm file1
718 $ rm file1
777 $ hg resolve --mark --config commands.resolve.mark-check=abort
719 $ hg resolve --mark --config commands.resolve.mark-check=abort
778 (no more unresolved files)
720 (no more unresolved files)
779 $ hg resolve --list
721 $ hg resolve --list
780 R file1
722 R file1
781 $ hg resolve --unmark file1
723 $ hg resolve --unmark file1
782 $ hg resolve file1
724 $ hg resolve file1
783 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
725 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
784 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
726 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
785 What do you want to do? u
727 What do you want to do? u
786 [1]
728 [1]
787 $ [ -f file1 ] || echo "File does not exist?"
729 $ [ -f file1 ] || echo "File does not exist?"
788 $ hg resolve --list
730 $ hg resolve --list
789 U file1
731 U file1
790 $ rm file1
732 $ rm file1
791 $ hg resolve --mark --config commands.resolve.mark-check=warn
733 $ hg resolve --mark --config commands.resolve.mark-check=warn
792 (no more unresolved files)
734 (no more unresolved files)
793 $ hg resolve --list
735 $ hg resolve --list
794 R file1
736 R file1
795
737
796 $ cd ..
738 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now