##// END OF EJS Templates
commit: warn the user when a commit already exists...
Dan Villiom Podlaski Christiansen -
r46799:976b26bd default
parent child Browse files
Show More
@@ -1,3896 +1,3901 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command 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 import copy as copymod
10 import copy as copymod
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 short,
19 short,
20 )
20 )
21 from .pycompat import (
21 from .pycompat import (
22 getattr,
22 getattr,
23 open,
23 open,
24 setattr,
24 setattr,
25 )
25 )
26 from .thirdparty import attr
26 from .thirdparty import attr
27
27
28 from . import (
28 from . import (
29 bookmarks,
29 bookmarks,
30 changelog,
30 changelog,
31 copies,
31 copies,
32 crecord as crecordmod,
32 crecord as crecordmod,
33 dirstateguard,
33 dirstateguard,
34 encoding,
34 encoding,
35 error,
35 error,
36 formatter,
36 formatter,
37 logcmdutil,
37 logcmdutil,
38 match as matchmod,
38 match as matchmod,
39 merge as mergemod,
39 merge as mergemod,
40 mergestate as mergestatemod,
40 mergestate as mergestatemod,
41 mergeutil,
41 mergeutil,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 pathutil,
44 pathutil,
45 phases,
45 phases,
46 pycompat,
46 pycompat,
47 repair,
47 repair,
48 revlog,
48 revlog,
49 rewriteutil,
49 rewriteutil,
50 scmutil,
50 scmutil,
51 state as statemod,
51 state as statemod,
52 subrepoutil,
52 subrepoutil,
53 templatekw,
53 templatekw,
54 templater,
54 templater,
55 util,
55 util,
56 vfs as vfsmod,
56 vfs as vfsmod,
57 )
57 )
58
58
59 from .utils import (
59 from .utils import (
60 dateutil,
60 dateutil,
61 stringutil,
61 stringutil,
62 )
62 )
63
63
64 if pycompat.TYPE_CHECKING:
64 if pycompat.TYPE_CHECKING:
65 from typing import (
65 from typing import (
66 Any,
66 Any,
67 Dict,
67 Dict,
68 )
68 )
69
69
70 for t in (Any, Dict):
70 for t in (Any, Dict):
71 assert t
71 assert t
72
72
73 stringio = util.stringio
73 stringio = util.stringio
74
74
75 # templates of common command options
75 # templates of common command options
76
76
77 dryrunopts = [
77 dryrunopts = [
78 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
78 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
79 ]
79 ]
80
80
81 confirmopts = [
81 confirmopts = [
82 (b'', b'confirm', None, _(b'ask before applying actions')),
82 (b'', b'confirm', None, _(b'ask before applying actions')),
83 ]
83 ]
84
84
85 remoteopts = [
85 remoteopts = [
86 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
86 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
87 (
87 (
88 b'',
88 b'',
89 b'remotecmd',
89 b'remotecmd',
90 b'',
90 b'',
91 _(b'specify hg command to run on the remote side'),
91 _(b'specify hg command to run on the remote side'),
92 _(b'CMD'),
92 _(b'CMD'),
93 ),
93 ),
94 (
94 (
95 b'',
95 b'',
96 b'insecure',
96 b'insecure',
97 None,
97 None,
98 _(b'do not verify server certificate (ignoring web.cacerts config)'),
98 _(b'do not verify server certificate (ignoring web.cacerts config)'),
99 ),
99 ),
100 ]
100 ]
101
101
102 walkopts = [
102 walkopts = [
103 (
103 (
104 b'I',
104 b'I',
105 b'include',
105 b'include',
106 [],
106 [],
107 _(b'include names matching the given patterns'),
107 _(b'include names matching the given patterns'),
108 _(b'PATTERN'),
108 _(b'PATTERN'),
109 ),
109 ),
110 (
110 (
111 b'X',
111 b'X',
112 b'exclude',
112 b'exclude',
113 [],
113 [],
114 _(b'exclude names matching the given patterns'),
114 _(b'exclude names matching the given patterns'),
115 _(b'PATTERN'),
115 _(b'PATTERN'),
116 ),
116 ),
117 ]
117 ]
118
118
119 commitopts = [
119 commitopts = [
120 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
120 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
121 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
121 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
122 ]
122 ]
123
123
124 commitopts2 = [
124 commitopts2 = [
125 (
125 (
126 b'd',
126 b'd',
127 b'date',
127 b'date',
128 b'',
128 b'',
129 _(b'record the specified date as commit date'),
129 _(b'record the specified date as commit date'),
130 _(b'DATE'),
130 _(b'DATE'),
131 ),
131 ),
132 (
132 (
133 b'u',
133 b'u',
134 b'user',
134 b'user',
135 b'',
135 b'',
136 _(b'record the specified user as committer'),
136 _(b'record the specified user as committer'),
137 _(b'USER'),
137 _(b'USER'),
138 ),
138 ),
139 ]
139 ]
140
140
141 commitopts3 = [
141 commitopts3 = [
142 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
142 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
143 (b'U', b'currentuser', None, _(b'record the current user as committer')),
143 (b'U', b'currentuser', None, _(b'record the current user as committer')),
144 ]
144 ]
145
145
146 formatteropts = [
146 formatteropts = [
147 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
147 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
148 ]
148 ]
149
149
150 templateopts = [
150 templateopts = [
151 (
151 (
152 b'',
152 b'',
153 b'style',
153 b'style',
154 b'',
154 b'',
155 _(b'display using template map file (DEPRECATED)'),
155 _(b'display using template map file (DEPRECATED)'),
156 _(b'STYLE'),
156 _(b'STYLE'),
157 ),
157 ),
158 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
158 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
159 ]
159 ]
160
160
161 logopts = [
161 logopts = [
162 (b'p', b'patch', None, _(b'show patch')),
162 (b'p', b'patch', None, _(b'show patch')),
163 (b'g', b'git', None, _(b'use git extended diff format')),
163 (b'g', b'git', None, _(b'use git extended diff format')),
164 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
164 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
165 (b'M', b'no-merges', None, _(b'do not show merges')),
165 (b'M', b'no-merges', None, _(b'do not show merges')),
166 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
166 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
167 (b'G', b'graph', None, _(b"show the revision DAG")),
167 (b'G', b'graph', None, _(b"show the revision DAG")),
168 ] + templateopts
168 ] + templateopts
169
169
170 diffopts = [
170 diffopts = [
171 (b'a', b'text', None, _(b'treat all files as text')),
171 (b'a', b'text', None, _(b'treat all files as text')),
172 (
172 (
173 b'g',
173 b'g',
174 b'git',
174 b'git',
175 None,
175 None,
176 _(b'use git extended diff format (DEFAULT: diff.git)'),
176 _(b'use git extended diff format (DEFAULT: diff.git)'),
177 ),
177 ),
178 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
178 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
179 (b'', b'nodates', None, _(b'omit dates from diff headers')),
179 (b'', b'nodates', None, _(b'omit dates from diff headers')),
180 ]
180 ]
181
181
182 diffwsopts = [
182 diffwsopts = [
183 (
183 (
184 b'w',
184 b'w',
185 b'ignore-all-space',
185 b'ignore-all-space',
186 None,
186 None,
187 _(b'ignore white space when comparing lines'),
187 _(b'ignore white space when comparing lines'),
188 ),
188 ),
189 (
189 (
190 b'b',
190 b'b',
191 b'ignore-space-change',
191 b'ignore-space-change',
192 None,
192 None,
193 _(b'ignore changes in the amount of white space'),
193 _(b'ignore changes in the amount of white space'),
194 ),
194 ),
195 (
195 (
196 b'B',
196 b'B',
197 b'ignore-blank-lines',
197 b'ignore-blank-lines',
198 None,
198 None,
199 _(b'ignore changes whose lines are all blank'),
199 _(b'ignore changes whose lines are all blank'),
200 ),
200 ),
201 (
201 (
202 b'Z',
202 b'Z',
203 b'ignore-space-at-eol',
203 b'ignore-space-at-eol',
204 None,
204 None,
205 _(b'ignore changes in whitespace at EOL'),
205 _(b'ignore changes in whitespace at EOL'),
206 ),
206 ),
207 ]
207 ]
208
208
209 diffopts2 = (
209 diffopts2 = (
210 [
210 [
211 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
211 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
212 (
212 (
213 b'p',
213 b'p',
214 b'show-function',
214 b'show-function',
215 None,
215 None,
216 _(
216 _(
217 b'show which function each change is in (DEFAULT: diff.showfunc)'
217 b'show which function each change is in (DEFAULT: diff.showfunc)'
218 ),
218 ),
219 ),
219 ),
220 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
220 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
221 ]
221 ]
222 + diffwsopts
222 + diffwsopts
223 + [
223 + [
224 (
224 (
225 b'U',
225 b'U',
226 b'unified',
226 b'unified',
227 b'',
227 b'',
228 _(b'number of lines of context to show'),
228 _(b'number of lines of context to show'),
229 _(b'NUM'),
229 _(b'NUM'),
230 ),
230 ),
231 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
231 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
232 (
232 (
233 b'',
233 b'',
234 b'root',
234 b'root',
235 b'',
235 b'',
236 _(b'produce diffs relative to subdirectory'),
236 _(b'produce diffs relative to subdirectory'),
237 _(b'DIR'),
237 _(b'DIR'),
238 ),
238 ),
239 ]
239 ]
240 )
240 )
241
241
242 mergetoolopts = [
242 mergetoolopts = [
243 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
243 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
244 ]
244 ]
245
245
246 similarityopts = [
246 similarityopts = [
247 (
247 (
248 b's',
248 b's',
249 b'similarity',
249 b'similarity',
250 b'',
250 b'',
251 _(b'guess renamed files by similarity (0<=s<=100)'),
251 _(b'guess renamed files by similarity (0<=s<=100)'),
252 _(b'SIMILARITY'),
252 _(b'SIMILARITY'),
253 )
253 )
254 ]
254 ]
255
255
256 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
256 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
257
257
258 debugrevlogopts = [
258 debugrevlogopts = [
259 (b'c', b'changelog', False, _(b'open changelog')),
259 (b'c', b'changelog', False, _(b'open changelog')),
260 (b'm', b'manifest', False, _(b'open manifest')),
260 (b'm', b'manifest', False, _(b'open manifest')),
261 (b'', b'dir', b'', _(b'open directory manifest')),
261 (b'', b'dir', b'', _(b'open directory manifest')),
262 ]
262 ]
263
263
264 # special string such that everything below this line will be ingored in the
264 # special string such that everything below this line will be ingored in the
265 # editor text
265 # editor text
266 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
266 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
267
267
268
268
269 def check_at_most_one_arg(opts, *args):
269 def check_at_most_one_arg(opts, *args):
270 """abort if more than one of the arguments are in opts
270 """abort if more than one of the arguments are in opts
271
271
272 Returns the unique argument or None if none of them were specified.
272 Returns the unique argument or None if none of them were specified.
273 """
273 """
274
274
275 def to_display(name):
275 def to_display(name):
276 return pycompat.sysbytes(name).replace(b'_', b'-')
276 return pycompat.sysbytes(name).replace(b'_', b'-')
277
277
278 previous = None
278 previous = None
279 for x in args:
279 for x in args:
280 if opts.get(x):
280 if opts.get(x):
281 if previous:
281 if previous:
282 raise error.Abort(
282 raise error.Abort(
283 _(b'cannot specify both --%s and --%s')
283 _(b'cannot specify both --%s and --%s')
284 % (to_display(previous), to_display(x))
284 % (to_display(previous), to_display(x))
285 )
285 )
286 previous = x
286 previous = x
287 return previous
287 return previous
288
288
289
289
290 def check_incompatible_arguments(opts, first, others):
290 def check_incompatible_arguments(opts, first, others):
291 """abort if the first argument is given along with any of the others
291 """abort if the first argument is given along with any of the others
292
292
293 Unlike check_at_most_one_arg(), `others` are not mutually exclusive
293 Unlike check_at_most_one_arg(), `others` are not mutually exclusive
294 among themselves, and they're passed as a single collection.
294 among themselves, and they're passed as a single collection.
295 """
295 """
296 for other in others:
296 for other in others:
297 check_at_most_one_arg(opts, first, other)
297 check_at_most_one_arg(opts, first, other)
298
298
299
299
300 def resolvecommitoptions(ui, opts):
300 def resolvecommitoptions(ui, opts):
301 """modify commit options dict to handle related options
301 """modify commit options dict to handle related options
302
302
303 The return value indicates that ``rewrite.update-timestamp`` is the reason
303 The return value indicates that ``rewrite.update-timestamp`` is the reason
304 the ``date`` option is set.
304 the ``date`` option is set.
305 """
305 """
306 check_at_most_one_arg(opts, b'date', b'currentdate')
306 check_at_most_one_arg(opts, b'date', b'currentdate')
307 check_at_most_one_arg(opts, b'user', b'currentuser')
307 check_at_most_one_arg(opts, b'user', b'currentuser')
308
308
309 datemaydiffer = False # date-only change should be ignored?
309 datemaydiffer = False # date-only change should be ignored?
310
310
311 if opts.get(b'currentdate'):
311 if opts.get(b'currentdate'):
312 opts[b'date'] = b'%d %d' % dateutil.makedate()
312 opts[b'date'] = b'%d %d' % dateutil.makedate()
313 elif (
313 elif (
314 not opts.get(b'date')
314 not opts.get(b'date')
315 and ui.configbool(b'rewrite', b'update-timestamp')
315 and ui.configbool(b'rewrite', b'update-timestamp')
316 and opts.get(b'currentdate') is None
316 and opts.get(b'currentdate') is None
317 ):
317 ):
318 opts[b'date'] = b'%d %d' % dateutil.makedate()
318 opts[b'date'] = b'%d %d' % dateutil.makedate()
319 datemaydiffer = True
319 datemaydiffer = True
320
320
321 if opts.get(b'currentuser'):
321 if opts.get(b'currentuser'):
322 opts[b'user'] = ui.username()
322 opts[b'user'] = ui.username()
323
323
324 return datemaydiffer
324 return datemaydiffer
325
325
326
326
327 def checknotesize(ui, opts):
327 def checknotesize(ui, opts):
328 """ make sure note is of valid format """
328 """ make sure note is of valid format """
329
329
330 note = opts.get(b'note')
330 note = opts.get(b'note')
331 if not note:
331 if not note:
332 return
332 return
333
333
334 if len(note) > 255:
334 if len(note) > 255:
335 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
335 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
336 if b'\n' in note:
336 if b'\n' in note:
337 raise error.Abort(_(b"note cannot contain a newline"))
337 raise error.Abort(_(b"note cannot contain a newline"))
338
338
339
339
340 def ishunk(x):
340 def ishunk(x):
341 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
341 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
342 return isinstance(x, hunkclasses)
342 return isinstance(x, hunkclasses)
343
343
344
344
345 def newandmodified(chunks, originalchunks):
345 def newandmodified(chunks, originalchunks):
346 newlyaddedandmodifiedfiles = set()
346 newlyaddedandmodifiedfiles = set()
347 alsorestore = set()
347 alsorestore = set()
348 for chunk in chunks:
348 for chunk in chunks:
349 if (
349 if (
350 ishunk(chunk)
350 ishunk(chunk)
351 and chunk.header.isnewfile()
351 and chunk.header.isnewfile()
352 and chunk not in originalchunks
352 and chunk not in originalchunks
353 ):
353 ):
354 newlyaddedandmodifiedfiles.add(chunk.header.filename())
354 newlyaddedandmodifiedfiles.add(chunk.header.filename())
355 alsorestore.update(
355 alsorestore.update(
356 set(chunk.header.files()) - {chunk.header.filename()}
356 set(chunk.header.files()) - {chunk.header.filename()}
357 )
357 )
358 return newlyaddedandmodifiedfiles, alsorestore
358 return newlyaddedandmodifiedfiles, alsorestore
359
359
360
360
361 def parsealiases(cmd):
361 def parsealiases(cmd):
362 return cmd.split(b"|")
362 return cmd.split(b"|")
363
363
364
364
365 def setupwrapcolorwrite(ui):
365 def setupwrapcolorwrite(ui):
366 # wrap ui.write so diff output can be labeled/colorized
366 # wrap ui.write so diff output can be labeled/colorized
367 def wrapwrite(orig, *args, **kw):
367 def wrapwrite(orig, *args, **kw):
368 label = kw.pop('label', b'')
368 label = kw.pop('label', b'')
369 for chunk, l in patch.difflabel(lambda: args):
369 for chunk, l in patch.difflabel(lambda: args):
370 orig(chunk, label=label + l)
370 orig(chunk, label=label + l)
371
371
372 oldwrite = ui.write
372 oldwrite = ui.write
373
373
374 def wrap(*args, **kwargs):
374 def wrap(*args, **kwargs):
375 return wrapwrite(oldwrite, *args, **kwargs)
375 return wrapwrite(oldwrite, *args, **kwargs)
376
376
377 setattr(ui, 'write', wrap)
377 setattr(ui, 'write', wrap)
378 return oldwrite
378 return oldwrite
379
379
380
380
381 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
381 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
382 try:
382 try:
383 if usecurses:
383 if usecurses:
384 if testfile:
384 if testfile:
385 recordfn = crecordmod.testdecorator(
385 recordfn = crecordmod.testdecorator(
386 testfile, crecordmod.testchunkselector
386 testfile, crecordmod.testchunkselector
387 )
387 )
388 else:
388 else:
389 recordfn = crecordmod.chunkselector
389 recordfn = crecordmod.chunkselector
390
390
391 return crecordmod.filterpatch(
391 return crecordmod.filterpatch(
392 ui, originalhunks, recordfn, operation
392 ui, originalhunks, recordfn, operation
393 )
393 )
394 except crecordmod.fallbackerror as e:
394 except crecordmod.fallbackerror as e:
395 ui.warn(b'%s\n' % e)
395 ui.warn(b'%s\n' % e)
396 ui.warn(_(b'falling back to text mode\n'))
396 ui.warn(_(b'falling back to text mode\n'))
397
397
398 return patch.filterpatch(ui, originalhunks, match, operation)
398 return patch.filterpatch(ui, originalhunks, match, operation)
399
399
400
400
401 def recordfilter(ui, originalhunks, match, operation=None):
401 def recordfilter(ui, originalhunks, match, operation=None):
402 """ Prompts the user to filter the originalhunks and return a list of
402 """ Prompts the user to filter the originalhunks and return a list of
403 selected hunks.
403 selected hunks.
404 *operation* is used for to build ui messages to indicate the user what
404 *operation* is used for to build ui messages to indicate the user what
405 kind of filtering they are doing: reverting, committing, shelving, etc.
405 kind of filtering they are doing: reverting, committing, shelving, etc.
406 (see patch.filterpatch).
406 (see patch.filterpatch).
407 """
407 """
408 usecurses = crecordmod.checkcurses(ui)
408 usecurses = crecordmod.checkcurses(ui)
409 testfile = ui.config(b'experimental', b'crecordtest')
409 testfile = ui.config(b'experimental', b'crecordtest')
410 oldwrite = setupwrapcolorwrite(ui)
410 oldwrite = setupwrapcolorwrite(ui)
411 try:
411 try:
412 newchunks, newopts = filterchunks(
412 newchunks, newopts = filterchunks(
413 ui, originalhunks, usecurses, testfile, match, operation
413 ui, originalhunks, usecurses, testfile, match, operation
414 )
414 )
415 finally:
415 finally:
416 ui.write = oldwrite
416 ui.write = oldwrite
417 return newchunks, newopts
417 return newchunks, newopts
418
418
419
419
420 def dorecord(
420 def dorecord(
421 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
421 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
422 ):
422 ):
423 opts = pycompat.byteskwargs(opts)
423 opts = pycompat.byteskwargs(opts)
424 if not ui.interactive():
424 if not ui.interactive():
425 if cmdsuggest:
425 if cmdsuggest:
426 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
426 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
427 else:
427 else:
428 msg = _(b'running non-interactively')
428 msg = _(b'running non-interactively')
429 raise error.Abort(msg)
429 raise error.Abort(msg)
430
430
431 # make sure username is set before going interactive
431 # make sure username is set before going interactive
432 if not opts.get(b'user'):
432 if not opts.get(b'user'):
433 ui.username() # raise exception, username not provided
433 ui.username() # raise exception, username not provided
434
434
435 def recordfunc(ui, repo, message, match, opts):
435 def recordfunc(ui, repo, message, match, opts):
436 """This is generic record driver.
436 """This is generic record driver.
437
437
438 Its job is to interactively filter local changes, and
438 Its job is to interactively filter local changes, and
439 accordingly prepare working directory into a state in which the
439 accordingly prepare working directory into a state in which the
440 job can be delegated to a non-interactive commit command such as
440 job can be delegated to a non-interactive commit command such as
441 'commit' or 'qrefresh'.
441 'commit' or 'qrefresh'.
442
442
443 After the actual job is done by non-interactive command, the
443 After the actual job is done by non-interactive command, the
444 working directory is restored to its original state.
444 working directory is restored to its original state.
445
445
446 In the end we'll record interesting changes, and everything else
446 In the end we'll record interesting changes, and everything else
447 will be left in place, so the user can continue working.
447 will be left in place, so the user can continue working.
448 """
448 """
449 if not opts.get(b'interactive-unshelve'):
449 if not opts.get(b'interactive-unshelve'):
450 checkunfinished(repo, commit=True)
450 checkunfinished(repo, commit=True)
451 wctx = repo[None]
451 wctx = repo[None]
452 merge = len(wctx.parents()) > 1
452 merge = len(wctx.parents()) > 1
453 if merge:
453 if merge:
454 raise error.Abort(
454 raise error.Abort(
455 _(
455 _(
456 b'cannot partially commit a merge '
456 b'cannot partially commit a merge '
457 b'(use "hg commit" instead)'
457 b'(use "hg commit" instead)'
458 )
458 )
459 )
459 )
460
460
461 def fail(f, msg):
461 def fail(f, msg):
462 raise error.Abort(b'%s: %s' % (f, msg))
462 raise error.Abort(b'%s: %s' % (f, msg))
463
463
464 force = opts.get(b'force')
464 force = opts.get(b'force')
465 if not force:
465 if not force:
466 match = matchmod.badmatch(match, fail)
466 match = matchmod.badmatch(match, fail)
467
467
468 status = repo.status(match=match)
468 status = repo.status(match=match)
469
469
470 overrides = {(b'ui', b'commitsubrepos'): True}
470 overrides = {(b'ui', b'commitsubrepos'): True}
471
471
472 with repo.ui.configoverride(overrides, b'record'):
472 with repo.ui.configoverride(overrides, b'record'):
473 # subrepoutil.precommit() modifies the status
473 # subrepoutil.precommit() modifies the status
474 tmpstatus = scmutil.status(
474 tmpstatus = scmutil.status(
475 copymod.copy(status.modified),
475 copymod.copy(status.modified),
476 copymod.copy(status.added),
476 copymod.copy(status.added),
477 copymod.copy(status.removed),
477 copymod.copy(status.removed),
478 copymod.copy(status.deleted),
478 copymod.copy(status.deleted),
479 copymod.copy(status.unknown),
479 copymod.copy(status.unknown),
480 copymod.copy(status.ignored),
480 copymod.copy(status.ignored),
481 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
481 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
482 )
482 )
483
483
484 # Force allows -X subrepo to skip the subrepo.
484 # Force allows -X subrepo to skip the subrepo.
485 subs, commitsubs, newstate = subrepoutil.precommit(
485 subs, commitsubs, newstate = subrepoutil.precommit(
486 repo.ui, wctx, tmpstatus, match, force=True
486 repo.ui, wctx, tmpstatus, match, force=True
487 )
487 )
488 for s in subs:
488 for s in subs:
489 if s in commitsubs:
489 if s in commitsubs:
490 dirtyreason = wctx.sub(s).dirtyreason(True)
490 dirtyreason = wctx.sub(s).dirtyreason(True)
491 raise error.Abort(dirtyreason)
491 raise error.Abort(dirtyreason)
492
492
493 if not force:
493 if not force:
494 repo.checkcommitpatterns(wctx, match, status, fail)
494 repo.checkcommitpatterns(wctx, match, status, fail)
495 diffopts = patch.difffeatureopts(
495 diffopts = patch.difffeatureopts(
496 ui,
496 ui,
497 opts=opts,
497 opts=opts,
498 whitespace=True,
498 whitespace=True,
499 section=b'commands',
499 section=b'commands',
500 configprefix=b'commit.interactive.',
500 configprefix=b'commit.interactive.',
501 )
501 )
502 diffopts.nodates = True
502 diffopts.nodates = True
503 diffopts.git = True
503 diffopts.git = True
504 diffopts.showfunc = True
504 diffopts.showfunc = True
505 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
505 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
506 originalchunks = patch.parsepatch(originaldiff)
506 originalchunks = patch.parsepatch(originaldiff)
507 match = scmutil.match(repo[None], pats)
507 match = scmutil.match(repo[None], pats)
508
508
509 # 1. filter patch, since we are intending to apply subset of it
509 # 1. filter patch, since we are intending to apply subset of it
510 try:
510 try:
511 chunks, newopts = filterfn(ui, originalchunks, match)
511 chunks, newopts = filterfn(ui, originalchunks, match)
512 except error.PatchError as err:
512 except error.PatchError as err:
513 raise error.Abort(_(b'error parsing patch: %s') % err)
513 raise error.Abort(_(b'error parsing patch: %s') % err)
514 opts.update(newopts)
514 opts.update(newopts)
515
515
516 # We need to keep a backup of files that have been newly added and
516 # We need to keep a backup of files that have been newly added and
517 # modified during the recording process because there is a previous
517 # modified during the recording process because there is a previous
518 # version without the edit in the workdir. We also will need to restore
518 # version without the edit in the workdir. We also will need to restore
519 # files that were the sources of renames so that the patch application
519 # files that were the sources of renames so that the patch application
520 # works.
520 # works.
521 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
521 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
522 chunks, originalchunks
522 chunks, originalchunks
523 )
523 )
524 contenders = set()
524 contenders = set()
525 for h in chunks:
525 for h in chunks:
526 try:
526 try:
527 contenders.update(set(h.files()))
527 contenders.update(set(h.files()))
528 except AttributeError:
528 except AttributeError:
529 pass
529 pass
530
530
531 changed = status.modified + status.added + status.removed
531 changed = status.modified + status.added + status.removed
532 newfiles = [f for f in changed if f in contenders]
532 newfiles = [f for f in changed if f in contenders]
533 if not newfiles:
533 if not newfiles:
534 ui.status(_(b'no changes to record\n'))
534 ui.status(_(b'no changes to record\n'))
535 return 0
535 return 0
536
536
537 modified = set(status.modified)
537 modified = set(status.modified)
538
538
539 # 2. backup changed files, so we can restore them in the end
539 # 2. backup changed files, so we can restore them in the end
540
540
541 if backupall:
541 if backupall:
542 tobackup = changed
542 tobackup = changed
543 else:
543 else:
544 tobackup = [
544 tobackup = [
545 f
545 f
546 for f in newfiles
546 for f in newfiles
547 if f in modified or f in newlyaddedandmodifiedfiles
547 if f in modified or f in newlyaddedandmodifiedfiles
548 ]
548 ]
549 backups = {}
549 backups = {}
550 if tobackup:
550 if tobackup:
551 backupdir = repo.vfs.join(b'record-backups')
551 backupdir = repo.vfs.join(b'record-backups')
552 try:
552 try:
553 os.mkdir(backupdir)
553 os.mkdir(backupdir)
554 except OSError as err:
554 except OSError as err:
555 if err.errno != errno.EEXIST:
555 if err.errno != errno.EEXIST:
556 raise
556 raise
557 try:
557 try:
558 # backup continues
558 # backup continues
559 for f in tobackup:
559 for f in tobackup:
560 fd, tmpname = pycompat.mkstemp(
560 fd, tmpname = pycompat.mkstemp(
561 prefix=os.path.basename(f) + b'.', dir=backupdir
561 prefix=os.path.basename(f) + b'.', dir=backupdir
562 )
562 )
563 os.close(fd)
563 os.close(fd)
564 ui.debug(b'backup %r as %r\n' % (f, tmpname))
564 ui.debug(b'backup %r as %r\n' % (f, tmpname))
565 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
565 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
566 backups[f] = tmpname
566 backups[f] = tmpname
567
567
568 fp = stringio()
568 fp = stringio()
569 for c in chunks:
569 for c in chunks:
570 fname = c.filename()
570 fname = c.filename()
571 if fname in backups:
571 if fname in backups:
572 c.write(fp)
572 c.write(fp)
573 dopatch = fp.tell()
573 dopatch = fp.tell()
574 fp.seek(0)
574 fp.seek(0)
575
575
576 # 2.5 optionally review / modify patch in text editor
576 # 2.5 optionally review / modify patch in text editor
577 if opts.get(b'review', False):
577 if opts.get(b'review', False):
578 patchtext = (
578 patchtext = (
579 crecordmod.diffhelptext
579 crecordmod.diffhelptext
580 + crecordmod.patchhelptext
580 + crecordmod.patchhelptext
581 + fp.read()
581 + fp.read()
582 )
582 )
583 reviewedpatch = ui.edit(
583 reviewedpatch = ui.edit(
584 patchtext, b"", action=b"diff", repopath=repo.path
584 patchtext, b"", action=b"diff", repopath=repo.path
585 )
585 )
586 fp.truncate(0)
586 fp.truncate(0)
587 fp.write(reviewedpatch)
587 fp.write(reviewedpatch)
588 fp.seek(0)
588 fp.seek(0)
589
589
590 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
590 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
591 # 3a. apply filtered patch to clean repo (clean)
591 # 3a. apply filtered patch to clean repo (clean)
592 if backups:
592 if backups:
593 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
593 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
594 mergemod.revert_to(repo[b'.'], matcher=m)
594 mergemod.revert_to(repo[b'.'], matcher=m)
595
595
596 # 3b. (apply)
596 # 3b. (apply)
597 if dopatch:
597 if dopatch:
598 try:
598 try:
599 ui.debug(b'applying patch\n')
599 ui.debug(b'applying patch\n')
600 ui.debug(fp.getvalue())
600 ui.debug(fp.getvalue())
601 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
601 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
602 except error.PatchError as err:
602 except error.PatchError as err:
603 raise error.Abort(pycompat.bytestr(err))
603 raise error.Abort(pycompat.bytestr(err))
604 del fp
604 del fp
605
605
606 # 4. We prepared working directory according to filtered
606 # 4. We prepared working directory according to filtered
607 # patch. Now is the time to delegate the job to
607 # patch. Now is the time to delegate the job to
608 # commit/qrefresh or the like!
608 # commit/qrefresh or the like!
609
609
610 # Make all of the pathnames absolute.
610 # Make all of the pathnames absolute.
611 newfiles = [repo.wjoin(nf) for nf in newfiles]
611 newfiles = [repo.wjoin(nf) for nf in newfiles]
612 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
612 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
613 finally:
613 finally:
614 # 5. finally restore backed-up files
614 # 5. finally restore backed-up files
615 try:
615 try:
616 dirstate = repo.dirstate
616 dirstate = repo.dirstate
617 for realname, tmpname in pycompat.iteritems(backups):
617 for realname, tmpname in pycompat.iteritems(backups):
618 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
618 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
619
619
620 if dirstate[realname] == b'n':
620 if dirstate[realname] == b'n':
621 # without normallookup, restoring timestamp
621 # without normallookup, restoring timestamp
622 # may cause partially committed files
622 # may cause partially committed files
623 # to be treated as unmodified
623 # to be treated as unmodified
624 dirstate.normallookup(realname)
624 dirstate.normallookup(realname)
625
625
626 # copystat=True here and above are a hack to trick any
626 # copystat=True here and above are a hack to trick any
627 # editors that have f open that we haven't modified them.
627 # editors that have f open that we haven't modified them.
628 #
628 #
629 # Also note that this racy as an editor could notice the
629 # Also note that this racy as an editor could notice the
630 # file's mtime before we've finished writing it.
630 # file's mtime before we've finished writing it.
631 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
631 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
632 os.unlink(tmpname)
632 os.unlink(tmpname)
633 if tobackup:
633 if tobackup:
634 os.rmdir(backupdir)
634 os.rmdir(backupdir)
635 except OSError:
635 except OSError:
636 pass
636 pass
637
637
638 def recordinwlock(ui, repo, message, match, opts):
638 def recordinwlock(ui, repo, message, match, opts):
639 with repo.wlock():
639 with repo.wlock():
640 return recordfunc(ui, repo, message, match, opts)
640 return recordfunc(ui, repo, message, match, opts)
641
641
642 return commit(ui, repo, recordinwlock, pats, opts)
642 return commit(ui, repo, recordinwlock, pats, opts)
643
643
644
644
645 class dirnode(object):
645 class dirnode(object):
646 """
646 """
647 Represent a directory in user working copy with information required for
647 Represent a directory in user working copy with information required for
648 the purpose of tersing its status.
648 the purpose of tersing its status.
649
649
650 path is the path to the directory, without a trailing '/'
650 path is the path to the directory, without a trailing '/'
651
651
652 statuses is a set of statuses of all files in this directory (this includes
652 statuses is a set of statuses of all files in this directory (this includes
653 all the files in all the subdirectories too)
653 all the files in all the subdirectories too)
654
654
655 files is a list of files which are direct child of this directory
655 files is a list of files which are direct child of this directory
656
656
657 subdirs is a dictionary of sub-directory name as the key and it's own
657 subdirs is a dictionary of sub-directory name as the key and it's own
658 dirnode object as the value
658 dirnode object as the value
659 """
659 """
660
660
661 def __init__(self, dirpath):
661 def __init__(self, dirpath):
662 self.path = dirpath
662 self.path = dirpath
663 self.statuses = set()
663 self.statuses = set()
664 self.files = []
664 self.files = []
665 self.subdirs = {}
665 self.subdirs = {}
666
666
667 def _addfileindir(self, filename, status):
667 def _addfileindir(self, filename, status):
668 """Add a file in this directory as a direct child."""
668 """Add a file in this directory as a direct child."""
669 self.files.append((filename, status))
669 self.files.append((filename, status))
670
670
671 def addfile(self, filename, status):
671 def addfile(self, filename, status):
672 """
672 """
673 Add a file to this directory or to its direct parent directory.
673 Add a file to this directory or to its direct parent directory.
674
674
675 If the file is not direct child of this directory, we traverse to the
675 If the file is not direct child of this directory, we traverse to the
676 directory of which this file is a direct child of and add the file
676 directory of which this file is a direct child of and add the file
677 there.
677 there.
678 """
678 """
679
679
680 # the filename contains a path separator, it means it's not the direct
680 # the filename contains a path separator, it means it's not the direct
681 # child of this directory
681 # child of this directory
682 if b'/' in filename:
682 if b'/' in filename:
683 subdir, filep = filename.split(b'/', 1)
683 subdir, filep = filename.split(b'/', 1)
684
684
685 # does the dirnode object for subdir exists
685 # does the dirnode object for subdir exists
686 if subdir not in self.subdirs:
686 if subdir not in self.subdirs:
687 subdirpath = pathutil.join(self.path, subdir)
687 subdirpath = pathutil.join(self.path, subdir)
688 self.subdirs[subdir] = dirnode(subdirpath)
688 self.subdirs[subdir] = dirnode(subdirpath)
689
689
690 # try adding the file in subdir
690 # try adding the file in subdir
691 self.subdirs[subdir].addfile(filep, status)
691 self.subdirs[subdir].addfile(filep, status)
692
692
693 else:
693 else:
694 self._addfileindir(filename, status)
694 self._addfileindir(filename, status)
695
695
696 if status not in self.statuses:
696 if status not in self.statuses:
697 self.statuses.add(status)
697 self.statuses.add(status)
698
698
699 def iterfilepaths(self):
699 def iterfilepaths(self):
700 """Yield (status, path) for files directly under this directory."""
700 """Yield (status, path) for files directly under this directory."""
701 for f, st in self.files:
701 for f, st in self.files:
702 yield st, pathutil.join(self.path, f)
702 yield st, pathutil.join(self.path, f)
703
703
704 def tersewalk(self, terseargs):
704 def tersewalk(self, terseargs):
705 """
705 """
706 Yield (status, path) obtained by processing the status of this
706 Yield (status, path) obtained by processing the status of this
707 dirnode.
707 dirnode.
708
708
709 terseargs is the string of arguments passed by the user with `--terse`
709 terseargs is the string of arguments passed by the user with `--terse`
710 flag.
710 flag.
711
711
712 Following are the cases which can happen:
712 Following are the cases which can happen:
713
713
714 1) All the files in the directory (including all the files in its
714 1) All the files in the directory (including all the files in its
715 subdirectories) share the same status and the user has asked us to terse
715 subdirectories) share the same status and the user has asked us to terse
716 that status. -> yield (status, dirpath). dirpath will end in '/'.
716 that status. -> yield (status, dirpath). dirpath will end in '/'.
717
717
718 2) Otherwise, we do following:
718 2) Otherwise, we do following:
719
719
720 a) Yield (status, filepath) for all the files which are in this
720 a) Yield (status, filepath) for all the files which are in this
721 directory (only the ones in this directory, not the subdirs)
721 directory (only the ones in this directory, not the subdirs)
722
722
723 b) Recurse the function on all the subdirectories of this
723 b) Recurse the function on all the subdirectories of this
724 directory
724 directory
725 """
725 """
726
726
727 if len(self.statuses) == 1:
727 if len(self.statuses) == 1:
728 onlyst = self.statuses.pop()
728 onlyst = self.statuses.pop()
729
729
730 # Making sure we terse only when the status abbreviation is
730 # Making sure we terse only when the status abbreviation is
731 # passed as terse argument
731 # passed as terse argument
732 if onlyst in terseargs:
732 if onlyst in terseargs:
733 yield onlyst, self.path + b'/'
733 yield onlyst, self.path + b'/'
734 return
734 return
735
735
736 # add the files to status list
736 # add the files to status list
737 for st, fpath in self.iterfilepaths():
737 for st, fpath in self.iterfilepaths():
738 yield st, fpath
738 yield st, fpath
739
739
740 # recurse on the subdirs
740 # recurse on the subdirs
741 for dirobj in self.subdirs.values():
741 for dirobj in self.subdirs.values():
742 for st, fpath in dirobj.tersewalk(terseargs):
742 for st, fpath in dirobj.tersewalk(terseargs):
743 yield st, fpath
743 yield st, fpath
744
744
745
745
746 def tersedir(statuslist, terseargs):
746 def tersedir(statuslist, terseargs):
747 """
747 """
748 Terse the status if all the files in a directory shares the same status.
748 Terse the status if all the files in a directory shares the same status.
749
749
750 statuslist is scmutil.status() object which contains a list of files for
750 statuslist is scmutil.status() object which contains a list of files for
751 each status.
751 each status.
752 terseargs is string which is passed by the user as the argument to `--terse`
752 terseargs is string which is passed by the user as the argument to `--terse`
753 flag.
753 flag.
754
754
755 The function makes a tree of objects of dirnode class, and at each node it
755 The function makes a tree of objects of dirnode class, and at each node it
756 stores the information required to know whether we can terse a certain
756 stores the information required to know whether we can terse a certain
757 directory or not.
757 directory or not.
758 """
758 """
759 # the order matters here as that is used to produce final list
759 # the order matters here as that is used to produce final list
760 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
760 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
761
761
762 # checking the argument validity
762 # checking the argument validity
763 for s in pycompat.bytestr(terseargs):
763 for s in pycompat.bytestr(terseargs):
764 if s not in allst:
764 if s not in allst:
765 raise error.Abort(_(b"'%s' not recognized") % s)
765 raise error.Abort(_(b"'%s' not recognized") % s)
766
766
767 # creating a dirnode object for the root of the repo
767 # creating a dirnode object for the root of the repo
768 rootobj = dirnode(b'')
768 rootobj = dirnode(b'')
769 pstatus = (
769 pstatus = (
770 b'modified',
770 b'modified',
771 b'added',
771 b'added',
772 b'deleted',
772 b'deleted',
773 b'clean',
773 b'clean',
774 b'unknown',
774 b'unknown',
775 b'ignored',
775 b'ignored',
776 b'removed',
776 b'removed',
777 )
777 )
778
778
779 tersedict = {}
779 tersedict = {}
780 for attrname in pstatus:
780 for attrname in pstatus:
781 statuschar = attrname[0:1]
781 statuschar = attrname[0:1]
782 for f in getattr(statuslist, attrname):
782 for f in getattr(statuslist, attrname):
783 rootobj.addfile(f, statuschar)
783 rootobj.addfile(f, statuschar)
784 tersedict[statuschar] = []
784 tersedict[statuschar] = []
785
785
786 # we won't be tersing the root dir, so add files in it
786 # we won't be tersing the root dir, so add files in it
787 for st, fpath in rootobj.iterfilepaths():
787 for st, fpath in rootobj.iterfilepaths():
788 tersedict[st].append(fpath)
788 tersedict[st].append(fpath)
789
789
790 # process each sub-directory and build tersedict
790 # process each sub-directory and build tersedict
791 for subdir in rootobj.subdirs.values():
791 for subdir in rootobj.subdirs.values():
792 for st, f in subdir.tersewalk(terseargs):
792 for st, f in subdir.tersewalk(terseargs):
793 tersedict[st].append(f)
793 tersedict[st].append(f)
794
794
795 tersedlist = []
795 tersedlist = []
796 for st in allst:
796 for st in allst:
797 tersedict[st].sort()
797 tersedict[st].sort()
798 tersedlist.append(tersedict[st])
798 tersedlist.append(tersedict[st])
799
799
800 return scmutil.status(*tersedlist)
800 return scmutil.status(*tersedlist)
801
801
802
802
803 def _commentlines(raw):
803 def _commentlines(raw):
804 '''Surround lineswith a comment char and a new line'''
804 '''Surround lineswith a comment char and a new line'''
805 lines = raw.splitlines()
805 lines = raw.splitlines()
806 commentedlines = [b'# %s' % line for line in lines]
806 commentedlines = [b'# %s' % line for line in lines]
807 return b'\n'.join(commentedlines) + b'\n'
807 return b'\n'.join(commentedlines) + b'\n'
808
808
809
809
810 @attr.s(frozen=True)
810 @attr.s(frozen=True)
811 class morestatus(object):
811 class morestatus(object):
812 reporoot = attr.ib()
812 reporoot = attr.ib()
813 unfinishedop = attr.ib()
813 unfinishedop = attr.ib()
814 unfinishedmsg = attr.ib()
814 unfinishedmsg = attr.ib()
815 activemerge = attr.ib()
815 activemerge = attr.ib()
816 unresolvedpaths = attr.ib()
816 unresolvedpaths = attr.ib()
817 _formattedpaths = attr.ib(init=False, default=set())
817 _formattedpaths = attr.ib(init=False, default=set())
818 _label = b'status.morestatus'
818 _label = b'status.morestatus'
819
819
820 def formatfile(self, path, fm):
820 def formatfile(self, path, fm):
821 self._formattedpaths.add(path)
821 self._formattedpaths.add(path)
822 if self.activemerge and path in self.unresolvedpaths:
822 if self.activemerge and path in self.unresolvedpaths:
823 fm.data(unresolved=True)
823 fm.data(unresolved=True)
824
824
825 def formatfooter(self, fm):
825 def formatfooter(self, fm):
826 if self.unfinishedop or self.unfinishedmsg:
826 if self.unfinishedop or self.unfinishedmsg:
827 fm.startitem()
827 fm.startitem()
828 fm.data(itemtype=b'morestatus')
828 fm.data(itemtype=b'morestatus')
829
829
830 if self.unfinishedop:
830 if self.unfinishedop:
831 fm.data(unfinished=self.unfinishedop)
831 fm.data(unfinished=self.unfinishedop)
832 statemsg = (
832 statemsg = (
833 _(b'The repository is in an unfinished *%s* state.')
833 _(b'The repository is in an unfinished *%s* state.')
834 % self.unfinishedop
834 % self.unfinishedop
835 )
835 )
836 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
836 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
837 if self.unfinishedmsg:
837 if self.unfinishedmsg:
838 fm.data(unfinishedmsg=self.unfinishedmsg)
838 fm.data(unfinishedmsg=self.unfinishedmsg)
839
839
840 # May also start new data items.
840 # May also start new data items.
841 self._formatconflicts(fm)
841 self._formatconflicts(fm)
842
842
843 if self.unfinishedmsg:
843 if self.unfinishedmsg:
844 fm.plain(
844 fm.plain(
845 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
845 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
846 )
846 )
847
847
848 def _formatconflicts(self, fm):
848 def _formatconflicts(self, fm):
849 if not self.activemerge:
849 if not self.activemerge:
850 return
850 return
851
851
852 if self.unresolvedpaths:
852 if self.unresolvedpaths:
853 mergeliststr = b'\n'.join(
853 mergeliststr = b'\n'.join(
854 [
854 [
855 b' %s'
855 b' %s'
856 % util.pathto(self.reporoot, encoding.getcwd(), path)
856 % util.pathto(self.reporoot, encoding.getcwd(), path)
857 for path in self.unresolvedpaths
857 for path in self.unresolvedpaths
858 ]
858 ]
859 )
859 )
860 msg = (
860 msg = (
861 _(
861 _(
862 '''Unresolved merge conflicts:
862 '''Unresolved merge conflicts:
863
863
864 %s
864 %s
865
865
866 To mark files as resolved: hg resolve --mark FILE'''
866 To mark files as resolved: hg resolve --mark FILE'''
867 )
867 )
868 % mergeliststr
868 % mergeliststr
869 )
869 )
870
870
871 # If any paths with unresolved conflicts were not previously
871 # If any paths with unresolved conflicts were not previously
872 # formatted, output them now.
872 # formatted, output them now.
873 for f in self.unresolvedpaths:
873 for f in self.unresolvedpaths:
874 if f in self._formattedpaths:
874 if f in self._formattedpaths:
875 # Already output.
875 # Already output.
876 continue
876 continue
877 fm.startitem()
877 fm.startitem()
878 # We can't claim to know the status of the file - it may just
878 # We can't claim to know the status of the file - it may just
879 # have been in one of the states that were not requested for
879 # have been in one of the states that were not requested for
880 # display, so it could be anything.
880 # display, so it could be anything.
881 fm.data(itemtype=b'file', path=f, unresolved=True)
881 fm.data(itemtype=b'file', path=f, unresolved=True)
882
882
883 else:
883 else:
884 msg = _(b'No unresolved merge conflicts.')
884 msg = _(b'No unresolved merge conflicts.')
885
885
886 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
886 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
887
887
888
888
889 def readmorestatus(repo):
889 def readmorestatus(repo):
890 """Returns a morestatus object if the repo has unfinished state."""
890 """Returns a morestatus object if the repo has unfinished state."""
891 statetuple = statemod.getrepostate(repo)
891 statetuple = statemod.getrepostate(repo)
892 mergestate = mergestatemod.mergestate.read(repo)
892 mergestate = mergestatemod.mergestate.read(repo)
893 activemerge = mergestate.active()
893 activemerge = mergestate.active()
894 if not statetuple and not activemerge:
894 if not statetuple and not activemerge:
895 return None
895 return None
896
896
897 unfinishedop = unfinishedmsg = unresolved = None
897 unfinishedop = unfinishedmsg = unresolved = None
898 if statetuple:
898 if statetuple:
899 unfinishedop, unfinishedmsg = statetuple
899 unfinishedop, unfinishedmsg = statetuple
900 if activemerge:
900 if activemerge:
901 unresolved = sorted(mergestate.unresolved())
901 unresolved = sorted(mergestate.unresolved())
902 return morestatus(
902 return morestatus(
903 repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved
903 repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved
904 )
904 )
905
905
906
906
907 def findpossible(cmd, table, strict=False):
907 def findpossible(cmd, table, strict=False):
908 """
908 """
909 Return cmd -> (aliases, command table entry)
909 Return cmd -> (aliases, command table entry)
910 for each matching command.
910 for each matching command.
911 Return debug commands (or their aliases) only if no normal command matches.
911 Return debug commands (or their aliases) only if no normal command matches.
912 """
912 """
913 choice = {}
913 choice = {}
914 debugchoice = {}
914 debugchoice = {}
915
915
916 if cmd in table:
916 if cmd in table:
917 # short-circuit exact matches, "log" alias beats "log|history"
917 # short-circuit exact matches, "log" alias beats "log|history"
918 keys = [cmd]
918 keys = [cmd]
919 else:
919 else:
920 keys = table.keys()
920 keys = table.keys()
921
921
922 allcmds = []
922 allcmds = []
923 for e in keys:
923 for e in keys:
924 aliases = parsealiases(e)
924 aliases = parsealiases(e)
925 allcmds.extend(aliases)
925 allcmds.extend(aliases)
926 found = None
926 found = None
927 if cmd in aliases:
927 if cmd in aliases:
928 found = cmd
928 found = cmd
929 elif not strict:
929 elif not strict:
930 for a in aliases:
930 for a in aliases:
931 if a.startswith(cmd):
931 if a.startswith(cmd):
932 found = a
932 found = a
933 break
933 break
934 if found is not None:
934 if found is not None:
935 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
935 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
936 debugchoice[found] = (aliases, table[e])
936 debugchoice[found] = (aliases, table[e])
937 else:
937 else:
938 choice[found] = (aliases, table[e])
938 choice[found] = (aliases, table[e])
939
939
940 if not choice and debugchoice:
940 if not choice and debugchoice:
941 choice = debugchoice
941 choice = debugchoice
942
942
943 return choice, allcmds
943 return choice, allcmds
944
944
945
945
946 def findcmd(cmd, table, strict=True):
946 def findcmd(cmd, table, strict=True):
947 """Return (aliases, command table entry) for command string."""
947 """Return (aliases, command table entry) for command string."""
948 choice, allcmds = findpossible(cmd, table, strict)
948 choice, allcmds = findpossible(cmd, table, strict)
949
949
950 if cmd in choice:
950 if cmd in choice:
951 return choice[cmd]
951 return choice[cmd]
952
952
953 if len(choice) > 1:
953 if len(choice) > 1:
954 clist = sorted(choice)
954 clist = sorted(choice)
955 raise error.AmbiguousCommand(cmd, clist)
955 raise error.AmbiguousCommand(cmd, clist)
956
956
957 if choice:
957 if choice:
958 return list(choice.values())[0]
958 return list(choice.values())[0]
959
959
960 raise error.UnknownCommand(cmd, allcmds)
960 raise error.UnknownCommand(cmd, allcmds)
961
961
962
962
963 def changebranch(ui, repo, revs, label, opts):
963 def changebranch(ui, repo, revs, label, opts):
964 """ Change the branch name of given revs to label """
964 """ Change the branch name of given revs to label """
965
965
966 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
966 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
967 # abort in case of uncommitted merge or dirty wdir
967 # abort in case of uncommitted merge or dirty wdir
968 bailifchanged(repo)
968 bailifchanged(repo)
969 revs = scmutil.revrange(repo, revs)
969 revs = scmutil.revrange(repo, revs)
970 if not revs:
970 if not revs:
971 raise error.Abort(b"empty revision set")
971 raise error.Abort(b"empty revision set")
972 roots = repo.revs(b'roots(%ld)', revs)
972 roots = repo.revs(b'roots(%ld)', revs)
973 if len(roots) > 1:
973 if len(roots) > 1:
974 raise error.Abort(
974 raise error.Abort(
975 _(b"cannot change branch of non-linear revisions")
975 _(b"cannot change branch of non-linear revisions")
976 )
976 )
977 rewriteutil.precheck(repo, revs, b'change branch of')
977 rewriteutil.precheck(repo, revs, b'change branch of')
978
978
979 root = repo[roots.first()]
979 root = repo[roots.first()]
980 rpb = {parent.branch() for parent in root.parents()}
980 rpb = {parent.branch() for parent in root.parents()}
981 if (
981 if (
982 not opts.get(b'force')
982 not opts.get(b'force')
983 and label not in rpb
983 and label not in rpb
984 and label in repo.branchmap()
984 and label in repo.branchmap()
985 ):
985 ):
986 raise error.Abort(_(b"a branch of the same name already exists"))
986 raise error.Abort(_(b"a branch of the same name already exists"))
987
987
988 if repo.revs(b'obsolete() and %ld', revs):
988 if repo.revs(b'obsolete() and %ld', revs):
989 raise error.Abort(
989 raise error.Abort(
990 _(b"cannot change branch of a obsolete changeset")
990 _(b"cannot change branch of a obsolete changeset")
991 )
991 )
992
992
993 # make sure only topological heads
993 # make sure only topological heads
994 if repo.revs(b'heads(%ld) - head()', revs):
994 if repo.revs(b'heads(%ld) - head()', revs):
995 raise error.Abort(_(b"cannot change branch in middle of a stack"))
995 raise error.Abort(_(b"cannot change branch in middle of a stack"))
996
996
997 replacements = {}
997 replacements = {}
998 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
998 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
999 # mercurial.subrepo -> mercurial.cmdutil
999 # mercurial.subrepo -> mercurial.cmdutil
1000 from . import context
1000 from . import context
1001
1001
1002 for rev in revs:
1002 for rev in revs:
1003 ctx = repo[rev]
1003 ctx = repo[rev]
1004 oldbranch = ctx.branch()
1004 oldbranch = ctx.branch()
1005 # check if ctx has same branch
1005 # check if ctx has same branch
1006 if oldbranch == label:
1006 if oldbranch == label:
1007 continue
1007 continue
1008
1008
1009 def filectxfn(repo, newctx, path):
1009 def filectxfn(repo, newctx, path):
1010 try:
1010 try:
1011 return ctx[path]
1011 return ctx[path]
1012 except error.ManifestLookupError:
1012 except error.ManifestLookupError:
1013 return None
1013 return None
1014
1014
1015 ui.debug(
1015 ui.debug(
1016 b"changing branch of '%s' from '%s' to '%s'\n"
1016 b"changing branch of '%s' from '%s' to '%s'\n"
1017 % (hex(ctx.node()), oldbranch, label)
1017 % (hex(ctx.node()), oldbranch, label)
1018 )
1018 )
1019 extra = ctx.extra()
1019 extra = ctx.extra()
1020 extra[b'branch_change'] = hex(ctx.node())
1020 extra[b'branch_change'] = hex(ctx.node())
1021 # While changing branch of set of linear commits, make sure that
1021 # While changing branch of set of linear commits, make sure that
1022 # we base our commits on new parent rather than old parent which
1022 # we base our commits on new parent rather than old parent which
1023 # was obsoleted while changing the branch
1023 # was obsoleted while changing the branch
1024 p1 = ctx.p1().node()
1024 p1 = ctx.p1().node()
1025 p2 = ctx.p2().node()
1025 p2 = ctx.p2().node()
1026 if p1 in replacements:
1026 if p1 in replacements:
1027 p1 = replacements[p1][0]
1027 p1 = replacements[p1][0]
1028 if p2 in replacements:
1028 if p2 in replacements:
1029 p2 = replacements[p2][0]
1029 p2 = replacements[p2][0]
1030
1030
1031 mc = context.memctx(
1031 mc = context.memctx(
1032 repo,
1032 repo,
1033 (p1, p2),
1033 (p1, p2),
1034 ctx.description(),
1034 ctx.description(),
1035 ctx.files(),
1035 ctx.files(),
1036 filectxfn,
1036 filectxfn,
1037 user=ctx.user(),
1037 user=ctx.user(),
1038 date=ctx.date(),
1038 date=ctx.date(),
1039 extra=extra,
1039 extra=extra,
1040 branch=label,
1040 branch=label,
1041 )
1041 )
1042
1042
1043 newnode = repo.commitctx(mc)
1043 newnode = repo.commitctx(mc)
1044 replacements[ctx.node()] = (newnode,)
1044 replacements[ctx.node()] = (newnode,)
1045 ui.debug(b'new node id is %s\n' % hex(newnode))
1045 ui.debug(b'new node id is %s\n' % hex(newnode))
1046
1046
1047 # create obsmarkers and move bookmarks
1047 # create obsmarkers and move bookmarks
1048 scmutil.cleanupnodes(
1048 scmutil.cleanupnodes(
1049 repo, replacements, b'branch-change', fixphase=True
1049 repo, replacements, b'branch-change', fixphase=True
1050 )
1050 )
1051
1051
1052 # move the working copy too
1052 # move the working copy too
1053 wctx = repo[None]
1053 wctx = repo[None]
1054 # in-progress merge is a bit too complex for now.
1054 # in-progress merge is a bit too complex for now.
1055 if len(wctx.parents()) == 1:
1055 if len(wctx.parents()) == 1:
1056 newid = replacements.get(wctx.p1().node())
1056 newid = replacements.get(wctx.p1().node())
1057 if newid is not None:
1057 if newid is not None:
1058 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1058 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1059 # mercurial.cmdutil
1059 # mercurial.cmdutil
1060 from . import hg
1060 from . import hg
1061
1061
1062 hg.update(repo, newid[0], quietempty=True)
1062 hg.update(repo, newid[0], quietempty=True)
1063
1063
1064 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1064 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1065
1065
1066
1066
1067 def findrepo(p):
1067 def findrepo(p):
1068 while not os.path.isdir(os.path.join(p, b".hg")):
1068 while not os.path.isdir(os.path.join(p, b".hg")):
1069 oldp, p = p, os.path.dirname(p)
1069 oldp, p = p, os.path.dirname(p)
1070 if p == oldp:
1070 if p == oldp:
1071 return None
1071 return None
1072
1072
1073 return p
1073 return p
1074
1074
1075
1075
1076 def bailifchanged(repo, merge=True, hint=None):
1076 def bailifchanged(repo, merge=True, hint=None):
1077 """ enforce the precondition that working directory must be clean.
1077 """ enforce the precondition that working directory must be clean.
1078
1078
1079 'merge' can be set to false if a pending uncommitted merge should be
1079 'merge' can be set to false if a pending uncommitted merge should be
1080 ignored (such as when 'update --check' runs).
1080 ignored (such as when 'update --check' runs).
1081
1081
1082 'hint' is the usual hint given to Abort exception.
1082 'hint' is the usual hint given to Abort exception.
1083 """
1083 """
1084
1084
1085 if merge and repo.dirstate.p2() != nullid:
1085 if merge and repo.dirstate.p2() != nullid:
1086 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1086 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1087 st = repo.status()
1087 st = repo.status()
1088 if st.modified or st.added or st.removed or st.deleted:
1088 if st.modified or st.added or st.removed or st.deleted:
1089 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1089 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1090 ctx = repo[None]
1090 ctx = repo[None]
1091 for s in sorted(ctx.substate):
1091 for s in sorted(ctx.substate):
1092 ctx.sub(s).bailifchanged(hint=hint)
1092 ctx.sub(s).bailifchanged(hint=hint)
1093
1093
1094
1094
1095 def logmessage(ui, opts):
1095 def logmessage(ui, opts):
1096 """ get the log message according to -m and -l option """
1096 """ get the log message according to -m and -l option """
1097
1097
1098 check_at_most_one_arg(opts, b'message', b'logfile')
1098 check_at_most_one_arg(opts, b'message', b'logfile')
1099
1099
1100 message = opts.get(b'message')
1100 message = opts.get(b'message')
1101 logfile = opts.get(b'logfile')
1101 logfile = opts.get(b'logfile')
1102
1102
1103 if not message and logfile:
1103 if not message and logfile:
1104 try:
1104 try:
1105 if isstdiofilename(logfile):
1105 if isstdiofilename(logfile):
1106 message = ui.fin.read()
1106 message = ui.fin.read()
1107 else:
1107 else:
1108 message = b'\n'.join(util.readfile(logfile).splitlines())
1108 message = b'\n'.join(util.readfile(logfile).splitlines())
1109 except IOError as inst:
1109 except IOError as inst:
1110 raise error.Abort(
1110 raise error.Abort(
1111 _(b"can't read commit message '%s': %s")
1111 _(b"can't read commit message '%s': %s")
1112 % (logfile, encoding.strtolocal(inst.strerror))
1112 % (logfile, encoding.strtolocal(inst.strerror))
1113 )
1113 )
1114 return message
1114 return message
1115
1115
1116
1116
1117 def mergeeditform(ctxorbool, baseformname):
1117 def mergeeditform(ctxorbool, baseformname):
1118 """return appropriate editform name (referencing a committemplate)
1118 """return appropriate editform name (referencing a committemplate)
1119
1119
1120 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1120 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1121 merging is committed.
1121 merging is committed.
1122
1122
1123 This returns baseformname with '.merge' appended if it is a merge,
1123 This returns baseformname with '.merge' appended if it is a merge,
1124 otherwise '.normal' is appended.
1124 otherwise '.normal' is appended.
1125 """
1125 """
1126 if isinstance(ctxorbool, bool):
1126 if isinstance(ctxorbool, bool):
1127 if ctxorbool:
1127 if ctxorbool:
1128 return baseformname + b".merge"
1128 return baseformname + b".merge"
1129 elif len(ctxorbool.parents()) > 1:
1129 elif len(ctxorbool.parents()) > 1:
1130 return baseformname + b".merge"
1130 return baseformname + b".merge"
1131
1131
1132 return baseformname + b".normal"
1132 return baseformname + b".normal"
1133
1133
1134
1134
1135 def getcommiteditor(
1135 def getcommiteditor(
1136 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1136 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1137 ):
1137 ):
1138 """get appropriate commit message editor according to '--edit' option
1138 """get appropriate commit message editor according to '--edit' option
1139
1139
1140 'finishdesc' is a function to be called with edited commit message
1140 'finishdesc' is a function to be called with edited commit message
1141 (= 'description' of the new changeset) just after editing, but
1141 (= 'description' of the new changeset) just after editing, but
1142 before checking empty-ness. It should return actual text to be
1142 before checking empty-ness. It should return actual text to be
1143 stored into history. This allows to change description before
1143 stored into history. This allows to change description before
1144 storing.
1144 storing.
1145
1145
1146 'extramsg' is a extra message to be shown in the editor instead of
1146 'extramsg' is a extra message to be shown in the editor instead of
1147 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1147 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1148 is automatically added.
1148 is automatically added.
1149
1149
1150 'editform' is a dot-separated list of names, to distinguish
1150 'editform' is a dot-separated list of names, to distinguish
1151 the purpose of commit text editing.
1151 the purpose of commit text editing.
1152
1152
1153 'getcommiteditor' returns 'commitforceeditor' regardless of
1153 'getcommiteditor' returns 'commitforceeditor' regardless of
1154 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1154 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1155 they are specific for usage in MQ.
1155 they are specific for usage in MQ.
1156 """
1156 """
1157 if edit or finishdesc or extramsg:
1157 if edit or finishdesc or extramsg:
1158 return lambda r, c, s: commitforceeditor(
1158 return lambda r, c, s: commitforceeditor(
1159 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1159 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1160 )
1160 )
1161 elif editform:
1161 elif editform:
1162 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1162 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1163 else:
1163 else:
1164 return commiteditor
1164 return commiteditor
1165
1165
1166
1166
1167 def _escapecommandtemplate(tmpl):
1167 def _escapecommandtemplate(tmpl):
1168 parts = []
1168 parts = []
1169 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1169 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1170 if typ == b'string':
1170 if typ == b'string':
1171 parts.append(stringutil.escapestr(tmpl[start:end]))
1171 parts.append(stringutil.escapestr(tmpl[start:end]))
1172 else:
1172 else:
1173 parts.append(tmpl[start:end])
1173 parts.append(tmpl[start:end])
1174 return b''.join(parts)
1174 return b''.join(parts)
1175
1175
1176
1176
1177 def rendercommandtemplate(ui, tmpl, props):
1177 def rendercommandtemplate(ui, tmpl, props):
1178 r"""Expand a literal template 'tmpl' in a way suitable for command line
1178 r"""Expand a literal template 'tmpl' in a way suitable for command line
1179
1179
1180 '\' in outermost string is not taken as an escape character because it
1180 '\' in outermost string is not taken as an escape character because it
1181 is a directory separator on Windows.
1181 is a directory separator on Windows.
1182
1182
1183 >>> from . import ui as uimod
1183 >>> from . import ui as uimod
1184 >>> ui = uimod.ui()
1184 >>> ui = uimod.ui()
1185 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1185 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1186 'c:\\foo'
1186 'c:\\foo'
1187 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1187 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1188 'c:{path}'
1188 'c:{path}'
1189 """
1189 """
1190 if not tmpl:
1190 if not tmpl:
1191 return tmpl
1191 return tmpl
1192 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1192 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1193 return t.renderdefault(props)
1193 return t.renderdefault(props)
1194
1194
1195
1195
1196 def rendertemplate(ctx, tmpl, props=None):
1196 def rendertemplate(ctx, tmpl, props=None):
1197 """Expand a literal template 'tmpl' byte-string against one changeset
1197 """Expand a literal template 'tmpl' byte-string against one changeset
1198
1198
1199 Each props item must be a stringify-able value or a callable returning
1199 Each props item must be a stringify-able value or a callable returning
1200 such value, i.e. no bare list nor dict should be passed.
1200 such value, i.e. no bare list nor dict should be passed.
1201 """
1201 """
1202 repo = ctx.repo()
1202 repo = ctx.repo()
1203 tres = formatter.templateresources(repo.ui, repo)
1203 tres = formatter.templateresources(repo.ui, repo)
1204 t = formatter.maketemplater(
1204 t = formatter.maketemplater(
1205 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1205 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1206 )
1206 )
1207 mapping = {b'ctx': ctx}
1207 mapping = {b'ctx': ctx}
1208 if props:
1208 if props:
1209 mapping.update(props)
1209 mapping.update(props)
1210 return t.renderdefault(mapping)
1210 return t.renderdefault(mapping)
1211
1211
1212
1212
1213 def format_changeset_summary(ui, ctx, command=None, default_spec=None):
1213 def format_changeset_summary(ui, ctx, command=None, default_spec=None):
1214 """Format a changeset summary (one line)."""
1214 """Format a changeset summary (one line)."""
1215 spec = None
1215 spec = None
1216 if command:
1216 if command:
1217 spec = ui.config(
1217 spec = ui.config(
1218 b'command-templates', b'oneline-summary.%s' % command, None
1218 b'command-templates', b'oneline-summary.%s' % command, None
1219 )
1219 )
1220 if not spec:
1220 if not spec:
1221 spec = ui.config(b'command-templates', b'oneline-summary')
1221 spec = ui.config(b'command-templates', b'oneline-summary')
1222 if not spec:
1222 if not spec:
1223 spec = default_spec
1223 spec = default_spec
1224 if not spec:
1224 if not spec:
1225 spec = (
1225 spec = (
1226 b'{separate(" ", '
1226 b'{separate(" ", '
1227 b'label("oneline-summary.changeset", "{rev}:{node|short}")'
1227 b'label("oneline-summary.changeset", "{rev}:{node|short}")'
1228 b', '
1228 b', '
1229 b'join(filter(namespaces % "{ifeq(namespace, "branches", "", join(names % "{label("oneline-summary.{namespace}", name)}", " "))}"), " ")'
1229 b'join(filter(namespaces % "{ifeq(namespace, "branches", "", join(names % "{label("oneline-summary.{namespace}", name)}", " "))}"), " ")'
1230 b')} '
1230 b')} '
1231 b'"{label("oneline-summary.desc", desc|firstline)}"'
1231 b'"{label("oneline-summary.desc", desc|firstline)}"'
1232 )
1232 )
1233 text = rendertemplate(ctx, spec)
1233 text = rendertemplate(ctx, spec)
1234 return text.split(b'\n')[0]
1234 return text.split(b'\n')[0]
1235
1235
1236
1236
1237 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1237 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1238 r"""Convert old-style filename format string to template string
1238 r"""Convert old-style filename format string to template string
1239
1239
1240 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1240 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1241 'foo-{reporoot|basename}-{seqno}.patch'
1241 'foo-{reporoot|basename}-{seqno}.patch'
1242 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1242 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1243 '{rev}{tags % "{tag}"}{node}'
1243 '{rev}{tags % "{tag}"}{node}'
1244
1244
1245 '\' in outermost strings has to be escaped because it is a directory
1245 '\' in outermost strings has to be escaped because it is a directory
1246 separator on Windows:
1246 separator on Windows:
1247
1247
1248 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1248 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1249 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1249 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1250 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1250 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1251 '\\\\\\\\foo\\\\bar.patch'
1251 '\\\\\\\\foo\\\\bar.patch'
1252 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1252 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1253 '\\\\{tags % "{tag}"}'
1253 '\\\\{tags % "{tag}"}'
1254
1254
1255 but inner strings follow the template rules (i.e. '\' is taken as an
1255 but inner strings follow the template rules (i.e. '\' is taken as an
1256 escape character):
1256 escape character):
1257
1257
1258 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1258 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1259 '{"c:\\tmp"}'
1259 '{"c:\\tmp"}'
1260 """
1260 """
1261 expander = {
1261 expander = {
1262 b'H': b'{node}',
1262 b'H': b'{node}',
1263 b'R': b'{rev}',
1263 b'R': b'{rev}',
1264 b'h': b'{node|short}',
1264 b'h': b'{node|short}',
1265 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1265 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1266 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1266 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1267 b'%': b'%',
1267 b'%': b'%',
1268 b'b': b'{reporoot|basename}',
1268 b'b': b'{reporoot|basename}',
1269 }
1269 }
1270 if total is not None:
1270 if total is not None:
1271 expander[b'N'] = b'{total}'
1271 expander[b'N'] = b'{total}'
1272 if seqno is not None:
1272 if seqno is not None:
1273 expander[b'n'] = b'{seqno}'
1273 expander[b'n'] = b'{seqno}'
1274 if total is not None and seqno is not None:
1274 if total is not None and seqno is not None:
1275 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1275 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1276 if pathname is not None:
1276 if pathname is not None:
1277 expander[b's'] = b'{pathname|basename}'
1277 expander[b's'] = b'{pathname|basename}'
1278 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1278 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1279 expander[b'p'] = b'{pathname}'
1279 expander[b'p'] = b'{pathname}'
1280
1280
1281 newname = []
1281 newname = []
1282 for typ, start, end in templater.scantemplate(pat, raw=True):
1282 for typ, start, end in templater.scantemplate(pat, raw=True):
1283 if typ != b'string':
1283 if typ != b'string':
1284 newname.append(pat[start:end])
1284 newname.append(pat[start:end])
1285 continue
1285 continue
1286 i = start
1286 i = start
1287 while i < end:
1287 while i < end:
1288 n = pat.find(b'%', i, end)
1288 n = pat.find(b'%', i, end)
1289 if n < 0:
1289 if n < 0:
1290 newname.append(stringutil.escapestr(pat[i:end]))
1290 newname.append(stringutil.escapestr(pat[i:end]))
1291 break
1291 break
1292 newname.append(stringutil.escapestr(pat[i:n]))
1292 newname.append(stringutil.escapestr(pat[i:n]))
1293 if n + 2 > end:
1293 if n + 2 > end:
1294 raise error.Abort(
1294 raise error.Abort(
1295 _(b"incomplete format spec in output filename")
1295 _(b"incomplete format spec in output filename")
1296 )
1296 )
1297 c = pat[n + 1 : n + 2]
1297 c = pat[n + 1 : n + 2]
1298 i = n + 2
1298 i = n + 2
1299 try:
1299 try:
1300 newname.append(expander[c])
1300 newname.append(expander[c])
1301 except KeyError:
1301 except KeyError:
1302 raise error.Abort(
1302 raise error.Abort(
1303 _(b"invalid format spec '%%%s' in output filename") % c
1303 _(b"invalid format spec '%%%s' in output filename") % c
1304 )
1304 )
1305 return b''.join(newname)
1305 return b''.join(newname)
1306
1306
1307
1307
1308 def makefilename(ctx, pat, **props):
1308 def makefilename(ctx, pat, **props):
1309 if not pat:
1309 if not pat:
1310 return pat
1310 return pat
1311 tmpl = _buildfntemplate(pat, **props)
1311 tmpl = _buildfntemplate(pat, **props)
1312 # BUG: alias expansion shouldn't be made against template fragments
1312 # BUG: alias expansion shouldn't be made against template fragments
1313 # rewritten from %-format strings, but we have no easy way to partially
1313 # rewritten from %-format strings, but we have no easy way to partially
1314 # disable the expansion.
1314 # disable the expansion.
1315 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1315 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1316
1316
1317
1317
1318 def isstdiofilename(pat):
1318 def isstdiofilename(pat):
1319 """True if the given pat looks like a filename denoting stdin/stdout"""
1319 """True if the given pat looks like a filename denoting stdin/stdout"""
1320 return not pat or pat == b'-'
1320 return not pat or pat == b'-'
1321
1321
1322
1322
1323 class _unclosablefile(object):
1323 class _unclosablefile(object):
1324 def __init__(self, fp):
1324 def __init__(self, fp):
1325 self._fp = fp
1325 self._fp = fp
1326
1326
1327 def close(self):
1327 def close(self):
1328 pass
1328 pass
1329
1329
1330 def __iter__(self):
1330 def __iter__(self):
1331 return iter(self._fp)
1331 return iter(self._fp)
1332
1332
1333 def __getattr__(self, attr):
1333 def __getattr__(self, attr):
1334 return getattr(self._fp, attr)
1334 return getattr(self._fp, attr)
1335
1335
1336 def __enter__(self):
1336 def __enter__(self):
1337 return self
1337 return self
1338
1338
1339 def __exit__(self, exc_type, exc_value, exc_tb):
1339 def __exit__(self, exc_type, exc_value, exc_tb):
1340 pass
1340 pass
1341
1341
1342
1342
1343 def makefileobj(ctx, pat, mode=b'wb', **props):
1343 def makefileobj(ctx, pat, mode=b'wb', **props):
1344 writable = mode not in (b'r', b'rb')
1344 writable = mode not in (b'r', b'rb')
1345
1345
1346 if isstdiofilename(pat):
1346 if isstdiofilename(pat):
1347 repo = ctx.repo()
1347 repo = ctx.repo()
1348 if writable:
1348 if writable:
1349 fp = repo.ui.fout
1349 fp = repo.ui.fout
1350 else:
1350 else:
1351 fp = repo.ui.fin
1351 fp = repo.ui.fin
1352 return _unclosablefile(fp)
1352 return _unclosablefile(fp)
1353 fn = makefilename(ctx, pat, **props)
1353 fn = makefilename(ctx, pat, **props)
1354 return open(fn, mode)
1354 return open(fn, mode)
1355
1355
1356
1356
1357 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1357 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1358 """opens the changelog, manifest, a filelog or a given revlog"""
1358 """opens the changelog, manifest, a filelog or a given revlog"""
1359 cl = opts[b'changelog']
1359 cl = opts[b'changelog']
1360 mf = opts[b'manifest']
1360 mf = opts[b'manifest']
1361 dir = opts[b'dir']
1361 dir = opts[b'dir']
1362 msg = None
1362 msg = None
1363 if cl and mf:
1363 if cl and mf:
1364 msg = _(b'cannot specify --changelog and --manifest at the same time')
1364 msg = _(b'cannot specify --changelog and --manifest at the same time')
1365 elif cl and dir:
1365 elif cl and dir:
1366 msg = _(b'cannot specify --changelog and --dir at the same time')
1366 msg = _(b'cannot specify --changelog and --dir at the same time')
1367 elif cl or mf or dir:
1367 elif cl or mf or dir:
1368 if file_:
1368 if file_:
1369 msg = _(b'cannot specify filename with --changelog or --manifest')
1369 msg = _(b'cannot specify filename with --changelog or --manifest')
1370 elif not repo:
1370 elif not repo:
1371 msg = _(
1371 msg = _(
1372 b'cannot specify --changelog or --manifest or --dir '
1372 b'cannot specify --changelog or --manifest or --dir '
1373 b'without a repository'
1373 b'without a repository'
1374 )
1374 )
1375 if msg:
1375 if msg:
1376 raise error.Abort(msg)
1376 raise error.Abort(msg)
1377
1377
1378 r = None
1378 r = None
1379 if repo:
1379 if repo:
1380 if cl:
1380 if cl:
1381 r = repo.unfiltered().changelog
1381 r = repo.unfiltered().changelog
1382 elif dir:
1382 elif dir:
1383 if not scmutil.istreemanifest(repo):
1383 if not scmutil.istreemanifest(repo):
1384 raise error.Abort(
1384 raise error.Abort(
1385 _(
1385 _(
1386 b"--dir can only be used on repos with "
1386 b"--dir can only be used on repos with "
1387 b"treemanifest enabled"
1387 b"treemanifest enabled"
1388 )
1388 )
1389 )
1389 )
1390 if not dir.endswith(b'/'):
1390 if not dir.endswith(b'/'):
1391 dir = dir + b'/'
1391 dir = dir + b'/'
1392 dirlog = repo.manifestlog.getstorage(dir)
1392 dirlog = repo.manifestlog.getstorage(dir)
1393 if len(dirlog):
1393 if len(dirlog):
1394 r = dirlog
1394 r = dirlog
1395 elif mf:
1395 elif mf:
1396 r = repo.manifestlog.getstorage(b'')
1396 r = repo.manifestlog.getstorage(b'')
1397 elif file_:
1397 elif file_:
1398 filelog = repo.file(file_)
1398 filelog = repo.file(file_)
1399 if len(filelog):
1399 if len(filelog):
1400 r = filelog
1400 r = filelog
1401
1401
1402 # Not all storage may be revlogs. If requested, try to return an actual
1402 # Not all storage may be revlogs. If requested, try to return an actual
1403 # revlog instance.
1403 # revlog instance.
1404 if returnrevlog:
1404 if returnrevlog:
1405 if isinstance(r, revlog.revlog):
1405 if isinstance(r, revlog.revlog):
1406 pass
1406 pass
1407 elif util.safehasattr(r, b'_revlog'):
1407 elif util.safehasattr(r, b'_revlog'):
1408 r = r._revlog # pytype: disable=attribute-error
1408 r = r._revlog # pytype: disable=attribute-error
1409 elif r is not None:
1409 elif r is not None:
1410 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1410 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1411
1411
1412 if not r:
1412 if not r:
1413 if not returnrevlog:
1413 if not returnrevlog:
1414 raise error.Abort(_(b'cannot give path to non-revlog'))
1414 raise error.Abort(_(b'cannot give path to non-revlog'))
1415
1415
1416 if not file_:
1416 if not file_:
1417 raise error.CommandError(cmd, _(b'invalid arguments'))
1417 raise error.CommandError(cmd, _(b'invalid arguments'))
1418 if not os.path.isfile(file_):
1418 if not os.path.isfile(file_):
1419 raise error.Abort(_(b"revlog '%s' not found") % file_)
1419 raise error.Abort(_(b"revlog '%s' not found") % file_)
1420 r = revlog.revlog(
1420 r = revlog.revlog(
1421 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1421 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1422 )
1422 )
1423 return r
1423 return r
1424
1424
1425
1425
1426 def openrevlog(repo, cmd, file_, opts):
1426 def openrevlog(repo, cmd, file_, opts):
1427 """Obtain a revlog backing storage of an item.
1427 """Obtain a revlog backing storage of an item.
1428
1428
1429 This is similar to ``openstorage()`` except it always returns a revlog.
1429 This is similar to ``openstorage()`` except it always returns a revlog.
1430
1430
1431 In most cases, a caller cares about the main storage object - not the
1431 In most cases, a caller cares about the main storage object - not the
1432 revlog backing it. Therefore, this function should only be used by code
1432 revlog backing it. Therefore, this function should only be used by code
1433 that needs to examine low-level revlog implementation details. e.g. debug
1433 that needs to examine low-level revlog implementation details. e.g. debug
1434 commands.
1434 commands.
1435 """
1435 """
1436 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1436 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1437
1437
1438
1438
1439 def copy(ui, repo, pats, opts, rename=False):
1439 def copy(ui, repo, pats, opts, rename=False):
1440 check_incompatible_arguments(opts, b'forget', [b'dry_run'])
1440 check_incompatible_arguments(opts, b'forget', [b'dry_run'])
1441
1441
1442 # called with the repo lock held
1442 # called with the repo lock held
1443 #
1443 #
1444 # hgsep => pathname that uses "/" to separate directories
1444 # hgsep => pathname that uses "/" to separate directories
1445 # ossep => pathname that uses os.sep to separate directories
1445 # ossep => pathname that uses os.sep to separate directories
1446 cwd = repo.getcwd()
1446 cwd = repo.getcwd()
1447 targets = {}
1447 targets = {}
1448 forget = opts.get(b"forget")
1448 forget = opts.get(b"forget")
1449 after = opts.get(b"after")
1449 after = opts.get(b"after")
1450 dryrun = opts.get(b"dry_run")
1450 dryrun = opts.get(b"dry_run")
1451 rev = opts.get(b'at_rev')
1451 rev = opts.get(b'at_rev')
1452 if rev:
1452 if rev:
1453 if not forget and not after:
1453 if not forget and not after:
1454 # TODO: Remove this restriction and make it also create the copy
1454 # TODO: Remove this restriction and make it also create the copy
1455 # targets (and remove the rename source if rename==True).
1455 # targets (and remove the rename source if rename==True).
1456 raise error.Abort(_(b'--at-rev requires --after'))
1456 raise error.Abort(_(b'--at-rev requires --after'))
1457 ctx = scmutil.revsingle(repo, rev)
1457 ctx = scmutil.revsingle(repo, rev)
1458 if len(ctx.parents()) > 1:
1458 if len(ctx.parents()) > 1:
1459 raise error.Abort(_(b'cannot mark/unmark copy in merge commit'))
1459 raise error.Abort(_(b'cannot mark/unmark copy in merge commit'))
1460 else:
1460 else:
1461 ctx = repo[None]
1461 ctx = repo[None]
1462
1462
1463 pctx = ctx.p1()
1463 pctx = ctx.p1()
1464
1464
1465 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1465 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1466
1466
1467 if forget:
1467 if forget:
1468 if ctx.rev() is None:
1468 if ctx.rev() is None:
1469 new_ctx = ctx
1469 new_ctx = ctx
1470 else:
1470 else:
1471 if len(ctx.parents()) > 1:
1471 if len(ctx.parents()) > 1:
1472 raise error.Abort(_(b'cannot unmark copy in merge commit'))
1472 raise error.Abort(_(b'cannot unmark copy in merge commit'))
1473 # avoid cycle context -> subrepo -> cmdutil
1473 # avoid cycle context -> subrepo -> cmdutil
1474 from . import context
1474 from . import context
1475
1475
1476 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1476 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1477 new_ctx = context.overlayworkingctx(repo)
1477 new_ctx = context.overlayworkingctx(repo)
1478 new_ctx.setbase(ctx.p1())
1478 new_ctx.setbase(ctx.p1())
1479 mergemod.graft(repo, ctx, wctx=new_ctx)
1479 mergemod.graft(repo, ctx, wctx=new_ctx)
1480
1480
1481 match = scmutil.match(ctx, pats, opts)
1481 match = scmutil.match(ctx, pats, opts)
1482
1482
1483 current_copies = ctx.p1copies()
1483 current_copies = ctx.p1copies()
1484 current_copies.update(ctx.p2copies())
1484 current_copies.update(ctx.p2copies())
1485
1485
1486 uipathfn = scmutil.getuipathfn(repo)
1486 uipathfn = scmutil.getuipathfn(repo)
1487 for f in ctx.walk(match):
1487 for f in ctx.walk(match):
1488 if f in current_copies:
1488 if f in current_copies:
1489 new_ctx[f].markcopied(None)
1489 new_ctx[f].markcopied(None)
1490 elif match.exact(f):
1490 elif match.exact(f):
1491 ui.warn(
1491 ui.warn(
1492 _(
1492 _(
1493 b'%s: not unmarking as copy - file is not marked as copied\n'
1493 b'%s: not unmarking as copy - file is not marked as copied\n'
1494 )
1494 )
1495 % uipathfn(f)
1495 % uipathfn(f)
1496 )
1496 )
1497
1497
1498 if ctx.rev() is not None:
1498 if ctx.rev() is not None:
1499 with repo.lock():
1499 with repo.lock():
1500 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1500 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1501 new_node = mem_ctx.commit()
1501 new_node = mem_ctx.commit()
1502
1502
1503 if repo.dirstate.p1() == ctx.node():
1503 if repo.dirstate.p1() == ctx.node():
1504 with repo.dirstate.parentchange():
1504 with repo.dirstate.parentchange():
1505 scmutil.movedirstate(repo, repo[new_node])
1505 scmutil.movedirstate(repo, repo[new_node])
1506 replacements = {ctx.node(): [new_node]}
1506 replacements = {ctx.node(): [new_node]}
1507 scmutil.cleanupnodes(
1507 scmutil.cleanupnodes(
1508 repo, replacements, b'uncopy', fixphase=True
1508 repo, replacements, b'uncopy', fixphase=True
1509 )
1509 )
1510
1510
1511 return
1511 return
1512
1512
1513 pats = scmutil.expandpats(pats)
1513 pats = scmutil.expandpats(pats)
1514 if not pats:
1514 if not pats:
1515 raise error.Abort(_(b'no source or destination specified'))
1515 raise error.Abort(_(b'no source or destination specified'))
1516 if len(pats) == 1:
1516 if len(pats) == 1:
1517 raise error.Abort(_(b'no destination specified'))
1517 raise error.Abort(_(b'no destination specified'))
1518 dest = pats.pop()
1518 dest = pats.pop()
1519
1519
1520 def walkpat(pat):
1520 def walkpat(pat):
1521 srcs = []
1521 srcs = []
1522 # TODO: Inline and simplify the non-working-copy version of this code
1522 # TODO: Inline and simplify the non-working-copy version of this code
1523 # since it shares very little with the working-copy version of it.
1523 # since it shares very little with the working-copy version of it.
1524 ctx_to_walk = ctx if ctx.rev() is None else pctx
1524 ctx_to_walk = ctx if ctx.rev() is None else pctx
1525 m = scmutil.match(ctx_to_walk, [pat], opts, globbed=True)
1525 m = scmutil.match(ctx_to_walk, [pat], opts, globbed=True)
1526 for abs in ctx_to_walk.walk(m):
1526 for abs in ctx_to_walk.walk(m):
1527 rel = uipathfn(abs)
1527 rel = uipathfn(abs)
1528 exact = m.exact(abs)
1528 exact = m.exact(abs)
1529 if abs not in ctx:
1529 if abs not in ctx:
1530 if abs in pctx:
1530 if abs in pctx:
1531 if not after:
1531 if not after:
1532 if exact:
1532 if exact:
1533 ui.warn(
1533 ui.warn(
1534 _(
1534 _(
1535 b'%s: not copying - file has been marked '
1535 b'%s: not copying - file has been marked '
1536 b'for remove\n'
1536 b'for remove\n'
1537 )
1537 )
1538 % rel
1538 % rel
1539 )
1539 )
1540 continue
1540 continue
1541 else:
1541 else:
1542 if exact:
1542 if exact:
1543 ui.warn(
1543 ui.warn(
1544 _(b'%s: not copying - file is not managed\n') % rel
1544 _(b'%s: not copying - file is not managed\n') % rel
1545 )
1545 )
1546 continue
1546 continue
1547
1547
1548 # abs: hgsep
1548 # abs: hgsep
1549 # rel: ossep
1549 # rel: ossep
1550 srcs.append((abs, rel, exact))
1550 srcs.append((abs, rel, exact))
1551 return srcs
1551 return srcs
1552
1552
1553 if ctx.rev() is not None:
1553 if ctx.rev() is not None:
1554 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1554 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1555 absdest = pathutil.canonpath(repo.root, cwd, dest)
1555 absdest = pathutil.canonpath(repo.root, cwd, dest)
1556 if ctx.hasdir(absdest):
1556 if ctx.hasdir(absdest):
1557 raise error.Abort(
1557 raise error.Abort(
1558 _(b'%s: --at-rev does not support a directory as destination')
1558 _(b'%s: --at-rev does not support a directory as destination')
1559 % uipathfn(absdest)
1559 % uipathfn(absdest)
1560 )
1560 )
1561 if absdest not in ctx:
1561 if absdest not in ctx:
1562 raise error.Abort(
1562 raise error.Abort(
1563 _(b'%s: copy destination does not exist in %s')
1563 _(b'%s: copy destination does not exist in %s')
1564 % (uipathfn(absdest), ctx)
1564 % (uipathfn(absdest), ctx)
1565 )
1565 )
1566
1566
1567 # avoid cycle context -> subrepo -> cmdutil
1567 # avoid cycle context -> subrepo -> cmdutil
1568 from . import context
1568 from . import context
1569
1569
1570 copylist = []
1570 copylist = []
1571 for pat in pats:
1571 for pat in pats:
1572 srcs = walkpat(pat)
1572 srcs = walkpat(pat)
1573 if not srcs:
1573 if not srcs:
1574 continue
1574 continue
1575 for abs, rel, exact in srcs:
1575 for abs, rel, exact in srcs:
1576 copylist.append(abs)
1576 copylist.append(abs)
1577
1577
1578 if not copylist:
1578 if not copylist:
1579 raise error.Abort(_(b'no files to copy'))
1579 raise error.Abort(_(b'no files to copy'))
1580 # TODO: Add support for `hg cp --at-rev . foo bar dir` and
1580 # TODO: Add support for `hg cp --at-rev . foo bar dir` and
1581 # `hg cp --at-rev . dir1 dir2`, preferably unifying the code with the
1581 # `hg cp --at-rev . dir1 dir2`, preferably unifying the code with the
1582 # existing functions below.
1582 # existing functions below.
1583 if len(copylist) != 1:
1583 if len(copylist) != 1:
1584 raise error.Abort(_(b'--at-rev requires a single source'))
1584 raise error.Abort(_(b'--at-rev requires a single source'))
1585
1585
1586 new_ctx = context.overlayworkingctx(repo)
1586 new_ctx = context.overlayworkingctx(repo)
1587 new_ctx.setbase(ctx.p1())
1587 new_ctx.setbase(ctx.p1())
1588 mergemod.graft(repo, ctx, wctx=new_ctx)
1588 mergemod.graft(repo, ctx, wctx=new_ctx)
1589
1589
1590 new_ctx.markcopied(absdest, copylist[0])
1590 new_ctx.markcopied(absdest, copylist[0])
1591
1591
1592 with repo.lock():
1592 with repo.lock():
1593 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1593 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1594 new_node = mem_ctx.commit()
1594 new_node = mem_ctx.commit()
1595
1595
1596 if repo.dirstate.p1() == ctx.node():
1596 if repo.dirstate.p1() == ctx.node():
1597 with repo.dirstate.parentchange():
1597 with repo.dirstate.parentchange():
1598 scmutil.movedirstate(repo, repo[new_node])
1598 scmutil.movedirstate(repo, repo[new_node])
1599 replacements = {ctx.node(): [new_node]}
1599 replacements = {ctx.node(): [new_node]}
1600 scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True)
1600 scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True)
1601
1601
1602 return
1602 return
1603
1603
1604 # abssrc: hgsep
1604 # abssrc: hgsep
1605 # relsrc: ossep
1605 # relsrc: ossep
1606 # otarget: ossep
1606 # otarget: ossep
1607 def copyfile(abssrc, relsrc, otarget, exact):
1607 def copyfile(abssrc, relsrc, otarget, exact):
1608 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1608 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1609 if b'/' in abstarget:
1609 if b'/' in abstarget:
1610 # We cannot normalize abstarget itself, this would prevent
1610 # We cannot normalize abstarget itself, this would prevent
1611 # case only renames, like a => A.
1611 # case only renames, like a => A.
1612 abspath, absname = abstarget.rsplit(b'/', 1)
1612 abspath, absname = abstarget.rsplit(b'/', 1)
1613 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1613 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1614 reltarget = repo.pathto(abstarget, cwd)
1614 reltarget = repo.pathto(abstarget, cwd)
1615 target = repo.wjoin(abstarget)
1615 target = repo.wjoin(abstarget)
1616 src = repo.wjoin(abssrc)
1616 src = repo.wjoin(abssrc)
1617 state = repo.dirstate[abstarget]
1617 state = repo.dirstate[abstarget]
1618
1618
1619 scmutil.checkportable(ui, abstarget)
1619 scmutil.checkportable(ui, abstarget)
1620
1620
1621 # check for collisions
1621 # check for collisions
1622 prevsrc = targets.get(abstarget)
1622 prevsrc = targets.get(abstarget)
1623 if prevsrc is not None:
1623 if prevsrc is not None:
1624 ui.warn(
1624 ui.warn(
1625 _(b'%s: not overwriting - %s collides with %s\n')
1625 _(b'%s: not overwriting - %s collides with %s\n')
1626 % (
1626 % (
1627 reltarget,
1627 reltarget,
1628 repo.pathto(abssrc, cwd),
1628 repo.pathto(abssrc, cwd),
1629 repo.pathto(prevsrc, cwd),
1629 repo.pathto(prevsrc, cwd),
1630 )
1630 )
1631 )
1631 )
1632 return True # report a failure
1632 return True # report a failure
1633
1633
1634 # check for overwrites
1634 # check for overwrites
1635 exists = os.path.lexists(target)
1635 exists = os.path.lexists(target)
1636 samefile = False
1636 samefile = False
1637 if exists and abssrc != abstarget:
1637 if exists and abssrc != abstarget:
1638 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1638 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1639 abstarget
1639 abstarget
1640 ):
1640 ):
1641 if not rename:
1641 if not rename:
1642 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1642 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1643 return True # report a failure
1643 return True # report a failure
1644 exists = False
1644 exists = False
1645 samefile = True
1645 samefile = True
1646
1646
1647 if not after and exists or after and state in b'mn':
1647 if not after and exists or after and state in b'mn':
1648 if not opts[b'force']:
1648 if not opts[b'force']:
1649 if state in b'mn':
1649 if state in b'mn':
1650 msg = _(b'%s: not overwriting - file already committed\n')
1650 msg = _(b'%s: not overwriting - file already committed\n')
1651 if after:
1651 if after:
1652 flags = b'--after --force'
1652 flags = b'--after --force'
1653 else:
1653 else:
1654 flags = b'--force'
1654 flags = b'--force'
1655 if rename:
1655 if rename:
1656 hint = (
1656 hint = (
1657 _(
1657 _(
1658 b"('hg rename %s' to replace the file by "
1658 b"('hg rename %s' to replace the file by "
1659 b'recording a rename)\n'
1659 b'recording a rename)\n'
1660 )
1660 )
1661 % flags
1661 % flags
1662 )
1662 )
1663 else:
1663 else:
1664 hint = (
1664 hint = (
1665 _(
1665 _(
1666 b"('hg copy %s' to replace the file by "
1666 b"('hg copy %s' to replace the file by "
1667 b'recording a copy)\n'
1667 b'recording a copy)\n'
1668 )
1668 )
1669 % flags
1669 % flags
1670 )
1670 )
1671 else:
1671 else:
1672 msg = _(b'%s: not overwriting - file exists\n')
1672 msg = _(b'%s: not overwriting - file exists\n')
1673 if rename:
1673 if rename:
1674 hint = _(
1674 hint = _(
1675 b"('hg rename --after' to record the rename)\n"
1675 b"('hg rename --after' to record the rename)\n"
1676 )
1676 )
1677 else:
1677 else:
1678 hint = _(b"('hg copy --after' to record the copy)\n")
1678 hint = _(b"('hg copy --after' to record the copy)\n")
1679 ui.warn(msg % reltarget)
1679 ui.warn(msg % reltarget)
1680 ui.warn(hint)
1680 ui.warn(hint)
1681 return True # report a failure
1681 return True # report a failure
1682
1682
1683 if after:
1683 if after:
1684 if not exists:
1684 if not exists:
1685 if rename:
1685 if rename:
1686 ui.warn(
1686 ui.warn(
1687 _(b'%s: not recording move - %s does not exist\n')
1687 _(b'%s: not recording move - %s does not exist\n')
1688 % (relsrc, reltarget)
1688 % (relsrc, reltarget)
1689 )
1689 )
1690 else:
1690 else:
1691 ui.warn(
1691 ui.warn(
1692 _(b'%s: not recording copy - %s does not exist\n')
1692 _(b'%s: not recording copy - %s does not exist\n')
1693 % (relsrc, reltarget)
1693 % (relsrc, reltarget)
1694 )
1694 )
1695 return True # report a failure
1695 return True # report a failure
1696 elif not dryrun:
1696 elif not dryrun:
1697 try:
1697 try:
1698 if exists:
1698 if exists:
1699 os.unlink(target)
1699 os.unlink(target)
1700 targetdir = os.path.dirname(target) or b'.'
1700 targetdir = os.path.dirname(target) or b'.'
1701 if not os.path.isdir(targetdir):
1701 if not os.path.isdir(targetdir):
1702 os.makedirs(targetdir)
1702 os.makedirs(targetdir)
1703 if samefile:
1703 if samefile:
1704 tmp = target + b"~hgrename"
1704 tmp = target + b"~hgrename"
1705 os.rename(src, tmp)
1705 os.rename(src, tmp)
1706 os.rename(tmp, target)
1706 os.rename(tmp, target)
1707 else:
1707 else:
1708 # Preserve stat info on renames, not on copies; this matches
1708 # Preserve stat info on renames, not on copies; this matches
1709 # Linux CLI behavior.
1709 # Linux CLI behavior.
1710 util.copyfile(src, target, copystat=rename)
1710 util.copyfile(src, target, copystat=rename)
1711 srcexists = True
1711 srcexists = True
1712 except IOError as inst:
1712 except IOError as inst:
1713 if inst.errno == errno.ENOENT:
1713 if inst.errno == errno.ENOENT:
1714 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1714 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1715 srcexists = False
1715 srcexists = False
1716 else:
1716 else:
1717 ui.warn(
1717 ui.warn(
1718 _(b'%s: cannot copy - %s\n')
1718 _(b'%s: cannot copy - %s\n')
1719 % (relsrc, encoding.strtolocal(inst.strerror))
1719 % (relsrc, encoding.strtolocal(inst.strerror))
1720 )
1720 )
1721 return True # report a failure
1721 return True # report a failure
1722
1722
1723 if ui.verbose or not exact:
1723 if ui.verbose or not exact:
1724 if rename:
1724 if rename:
1725 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1725 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1726 else:
1726 else:
1727 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1727 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1728
1728
1729 targets[abstarget] = abssrc
1729 targets[abstarget] = abssrc
1730
1730
1731 # fix up dirstate
1731 # fix up dirstate
1732 scmutil.dirstatecopy(
1732 scmutil.dirstatecopy(
1733 ui, repo, ctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1733 ui, repo, ctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1734 )
1734 )
1735 if rename and not dryrun:
1735 if rename and not dryrun:
1736 if not after and srcexists and not samefile:
1736 if not after and srcexists and not samefile:
1737 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1737 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1738 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1738 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1739 ctx.forget([abssrc])
1739 ctx.forget([abssrc])
1740
1740
1741 # pat: ossep
1741 # pat: ossep
1742 # dest ossep
1742 # dest ossep
1743 # srcs: list of (hgsep, hgsep, ossep, bool)
1743 # srcs: list of (hgsep, hgsep, ossep, bool)
1744 # return: function that takes hgsep and returns ossep
1744 # return: function that takes hgsep and returns ossep
1745 def targetpathfn(pat, dest, srcs):
1745 def targetpathfn(pat, dest, srcs):
1746 if os.path.isdir(pat):
1746 if os.path.isdir(pat):
1747 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1747 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1748 abspfx = util.localpath(abspfx)
1748 abspfx = util.localpath(abspfx)
1749 if destdirexists:
1749 if destdirexists:
1750 striplen = len(os.path.split(abspfx)[0])
1750 striplen = len(os.path.split(abspfx)[0])
1751 else:
1751 else:
1752 striplen = len(abspfx)
1752 striplen = len(abspfx)
1753 if striplen:
1753 if striplen:
1754 striplen += len(pycompat.ossep)
1754 striplen += len(pycompat.ossep)
1755 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1755 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1756 elif destdirexists:
1756 elif destdirexists:
1757 res = lambda p: os.path.join(
1757 res = lambda p: os.path.join(
1758 dest, os.path.basename(util.localpath(p))
1758 dest, os.path.basename(util.localpath(p))
1759 )
1759 )
1760 else:
1760 else:
1761 res = lambda p: dest
1761 res = lambda p: dest
1762 return res
1762 return res
1763
1763
1764 # pat: ossep
1764 # pat: ossep
1765 # dest ossep
1765 # dest ossep
1766 # srcs: list of (hgsep, hgsep, ossep, bool)
1766 # srcs: list of (hgsep, hgsep, ossep, bool)
1767 # return: function that takes hgsep and returns ossep
1767 # return: function that takes hgsep and returns ossep
1768 def targetpathafterfn(pat, dest, srcs):
1768 def targetpathafterfn(pat, dest, srcs):
1769 if matchmod.patkind(pat):
1769 if matchmod.patkind(pat):
1770 # a mercurial pattern
1770 # a mercurial pattern
1771 res = lambda p: os.path.join(
1771 res = lambda p: os.path.join(
1772 dest, os.path.basename(util.localpath(p))
1772 dest, os.path.basename(util.localpath(p))
1773 )
1773 )
1774 else:
1774 else:
1775 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1775 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1776 if len(abspfx) < len(srcs[0][0]):
1776 if len(abspfx) < len(srcs[0][0]):
1777 # A directory. Either the target path contains the last
1777 # A directory. Either the target path contains the last
1778 # component of the source path or it does not.
1778 # component of the source path or it does not.
1779 def evalpath(striplen):
1779 def evalpath(striplen):
1780 score = 0
1780 score = 0
1781 for s in srcs:
1781 for s in srcs:
1782 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1782 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1783 if os.path.lexists(t):
1783 if os.path.lexists(t):
1784 score += 1
1784 score += 1
1785 return score
1785 return score
1786
1786
1787 abspfx = util.localpath(abspfx)
1787 abspfx = util.localpath(abspfx)
1788 striplen = len(abspfx)
1788 striplen = len(abspfx)
1789 if striplen:
1789 if striplen:
1790 striplen += len(pycompat.ossep)
1790 striplen += len(pycompat.ossep)
1791 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1791 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1792 score = evalpath(striplen)
1792 score = evalpath(striplen)
1793 striplen1 = len(os.path.split(abspfx)[0])
1793 striplen1 = len(os.path.split(abspfx)[0])
1794 if striplen1:
1794 if striplen1:
1795 striplen1 += len(pycompat.ossep)
1795 striplen1 += len(pycompat.ossep)
1796 if evalpath(striplen1) > score:
1796 if evalpath(striplen1) > score:
1797 striplen = striplen1
1797 striplen = striplen1
1798 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1798 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1799 else:
1799 else:
1800 # a file
1800 # a file
1801 if destdirexists:
1801 if destdirexists:
1802 res = lambda p: os.path.join(
1802 res = lambda p: os.path.join(
1803 dest, os.path.basename(util.localpath(p))
1803 dest, os.path.basename(util.localpath(p))
1804 )
1804 )
1805 else:
1805 else:
1806 res = lambda p: dest
1806 res = lambda p: dest
1807 return res
1807 return res
1808
1808
1809 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1809 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1810 if not destdirexists:
1810 if not destdirexists:
1811 if len(pats) > 1 or matchmod.patkind(pats[0]):
1811 if len(pats) > 1 or matchmod.patkind(pats[0]):
1812 raise error.Abort(
1812 raise error.Abort(
1813 _(
1813 _(
1814 b'with multiple sources, destination must be an '
1814 b'with multiple sources, destination must be an '
1815 b'existing directory'
1815 b'existing directory'
1816 )
1816 )
1817 )
1817 )
1818 if util.endswithsep(dest):
1818 if util.endswithsep(dest):
1819 raise error.Abort(_(b'destination %s is not a directory') % dest)
1819 raise error.Abort(_(b'destination %s is not a directory') % dest)
1820
1820
1821 tfn = targetpathfn
1821 tfn = targetpathfn
1822 if after:
1822 if after:
1823 tfn = targetpathafterfn
1823 tfn = targetpathafterfn
1824 copylist = []
1824 copylist = []
1825 for pat in pats:
1825 for pat in pats:
1826 srcs = walkpat(pat)
1826 srcs = walkpat(pat)
1827 if not srcs:
1827 if not srcs:
1828 continue
1828 continue
1829 copylist.append((tfn(pat, dest, srcs), srcs))
1829 copylist.append((tfn(pat, dest, srcs), srcs))
1830 if not copylist:
1830 if not copylist:
1831 raise error.Abort(_(b'no files to copy'))
1831 raise error.Abort(_(b'no files to copy'))
1832
1832
1833 errors = 0
1833 errors = 0
1834 for targetpath, srcs in copylist:
1834 for targetpath, srcs in copylist:
1835 for abssrc, relsrc, exact in srcs:
1835 for abssrc, relsrc, exact in srcs:
1836 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1836 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1837 errors += 1
1837 errors += 1
1838
1838
1839 return errors != 0
1839 return errors != 0
1840
1840
1841
1841
1842 ## facility to let extension process additional data into an import patch
1842 ## facility to let extension process additional data into an import patch
1843 # list of identifier to be executed in order
1843 # list of identifier to be executed in order
1844 extrapreimport = [] # run before commit
1844 extrapreimport = [] # run before commit
1845 extrapostimport = [] # run after commit
1845 extrapostimport = [] # run after commit
1846 # mapping from identifier to actual import function
1846 # mapping from identifier to actual import function
1847 #
1847 #
1848 # 'preimport' are run before the commit is made and are provided the following
1848 # 'preimport' are run before the commit is made and are provided the following
1849 # arguments:
1849 # arguments:
1850 # - repo: the localrepository instance,
1850 # - repo: the localrepository instance,
1851 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1851 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1852 # - extra: the future extra dictionary of the changeset, please mutate it,
1852 # - extra: the future extra dictionary of the changeset, please mutate it,
1853 # - opts: the import options.
1853 # - opts: the import options.
1854 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1854 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1855 # mutation of in memory commit and more. Feel free to rework the code to get
1855 # mutation of in memory commit and more. Feel free to rework the code to get
1856 # there.
1856 # there.
1857 extrapreimportmap = {}
1857 extrapreimportmap = {}
1858 # 'postimport' are run after the commit is made and are provided the following
1858 # 'postimport' are run after the commit is made and are provided the following
1859 # argument:
1859 # argument:
1860 # - ctx: the changectx created by import.
1860 # - ctx: the changectx created by import.
1861 extrapostimportmap = {}
1861 extrapostimportmap = {}
1862
1862
1863
1863
1864 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1864 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1865 """Utility function used by commands.import to import a single patch
1865 """Utility function used by commands.import to import a single patch
1866
1866
1867 This function is explicitly defined here to help the evolve extension to
1867 This function is explicitly defined here to help the evolve extension to
1868 wrap this part of the import logic.
1868 wrap this part of the import logic.
1869
1869
1870 The API is currently a bit ugly because it a simple code translation from
1870 The API is currently a bit ugly because it a simple code translation from
1871 the import command. Feel free to make it better.
1871 the import command. Feel free to make it better.
1872
1872
1873 :patchdata: a dictionary containing parsed patch data (such as from
1873 :patchdata: a dictionary containing parsed patch data (such as from
1874 ``patch.extract()``)
1874 ``patch.extract()``)
1875 :parents: nodes that will be parent of the created commit
1875 :parents: nodes that will be parent of the created commit
1876 :opts: the full dict of option passed to the import command
1876 :opts: the full dict of option passed to the import command
1877 :msgs: list to save commit message to.
1877 :msgs: list to save commit message to.
1878 (used in case we need to save it when failing)
1878 (used in case we need to save it when failing)
1879 :updatefunc: a function that update a repo to a given node
1879 :updatefunc: a function that update a repo to a given node
1880 updatefunc(<repo>, <node>)
1880 updatefunc(<repo>, <node>)
1881 """
1881 """
1882 # avoid cycle context -> subrepo -> cmdutil
1882 # avoid cycle context -> subrepo -> cmdutil
1883 from . import context
1883 from . import context
1884
1884
1885 tmpname = patchdata.get(b'filename')
1885 tmpname = patchdata.get(b'filename')
1886 message = patchdata.get(b'message')
1886 message = patchdata.get(b'message')
1887 user = opts.get(b'user') or patchdata.get(b'user')
1887 user = opts.get(b'user') or patchdata.get(b'user')
1888 date = opts.get(b'date') or patchdata.get(b'date')
1888 date = opts.get(b'date') or patchdata.get(b'date')
1889 branch = patchdata.get(b'branch')
1889 branch = patchdata.get(b'branch')
1890 nodeid = patchdata.get(b'nodeid')
1890 nodeid = patchdata.get(b'nodeid')
1891 p1 = patchdata.get(b'p1')
1891 p1 = patchdata.get(b'p1')
1892 p2 = patchdata.get(b'p2')
1892 p2 = patchdata.get(b'p2')
1893
1893
1894 nocommit = opts.get(b'no_commit')
1894 nocommit = opts.get(b'no_commit')
1895 importbranch = opts.get(b'import_branch')
1895 importbranch = opts.get(b'import_branch')
1896 update = not opts.get(b'bypass')
1896 update = not opts.get(b'bypass')
1897 strip = opts[b"strip"]
1897 strip = opts[b"strip"]
1898 prefix = opts[b"prefix"]
1898 prefix = opts[b"prefix"]
1899 sim = float(opts.get(b'similarity') or 0)
1899 sim = float(opts.get(b'similarity') or 0)
1900
1900
1901 if not tmpname:
1901 if not tmpname:
1902 return None, None, False
1902 return None, None, False
1903
1903
1904 rejects = False
1904 rejects = False
1905
1905
1906 cmdline_message = logmessage(ui, opts)
1906 cmdline_message = logmessage(ui, opts)
1907 if cmdline_message:
1907 if cmdline_message:
1908 # pickup the cmdline msg
1908 # pickup the cmdline msg
1909 message = cmdline_message
1909 message = cmdline_message
1910 elif message:
1910 elif message:
1911 # pickup the patch msg
1911 # pickup the patch msg
1912 message = message.strip()
1912 message = message.strip()
1913 else:
1913 else:
1914 # launch the editor
1914 # launch the editor
1915 message = None
1915 message = None
1916 ui.debug(b'message:\n%s\n' % (message or b''))
1916 ui.debug(b'message:\n%s\n' % (message or b''))
1917
1917
1918 if len(parents) == 1:
1918 if len(parents) == 1:
1919 parents.append(repo[nullid])
1919 parents.append(repo[nullid])
1920 if opts.get(b'exact'):
1920 if opts.get(b'exact'):
1921 if not nodeid or not p1:
1921 if not nodeid or not p1:
1922 raise error.Abort(_(b'not a Mercurial patch'))
1922 raise error.Abort(_(b'not a Mercurial patch'))
1923 p1 = repo[p1]
1923 p1 = repo[p1]
1924 p2 = repo[p2 or nullid]
1924 p2 = repo[p2 or nullid]
1925 elif p2:
1925 elif p2:
1926 try:
1926 try:
1927 p1 = repo[p1]
1927 p1 = repo[p1]
1928 p2 = repo[p2]
1928 p2 = repo[p2]
1929 # Without any options, consider p2 only if the
1929 # Without any options, consider p2 only if the
1930 # patch is being applied on top of the recorded
1930 # patch is being applied on top of the recorded
1931 # first parent.
1931 # first parent.
1932 if p1 != parents[0]:
1932 if p1 != parents[0]:
1933 p1 = parents[0]
1933 p1 = parents[0]
1934 p2 = repo[nullid]
1934 p2 = repo[nullid]
1935 except error.RepoError:
1935 except error.RepoError:
1936 p1, p2 = parents
1936 p1, p2 = parents
1937 if p2.node() == nullid:
1937 if p2.node() == nullid:
1938 ui.warn(
1938 ui.warn(
1939 _(
1939 _(
1940 b"warning: import the patch as a normal revision\n"
1940 b"warning: import the patch as a normal revision\n"
1941 b"(use --exact to import the patch as a merge)\n"
1941 b"(use --exact to import the patch as a merge)\n"
1942 )
1942 )
1943 )
1943 )
1944 else:
1944 else:
1945 p1, p2 = parents
1945 p1, p2 = parents
1946
1946
1947 n = None
1947 n = None
1948 if update:
1948 if update:
1949 if p1 != parents[0]:
1949 if p1 != parents[0]:
1950 updatefunc(repo, p1.node())
1950 updatefunc(repo, p1.node())
1951 if p2 != parents[1]:
1951 if p2 != parents[1]:
1952 repo.setparents(p1.node(), p2.node())
1952 repo.setparents(p1.node(), p2.node())
1953
1953
1954 if opts.get(b'exact') or importbranch:
1954 if opts.get(b'exact') or importbranch:
1955 repo.dirstate.setbranch(branch or b'default')
1955 repo.dirstate.setbranch(branch or b'default')
1956
1956
1957 partial = opts.get(b'partial', False)
1957 partial = opts.get(b'partial', False)
1958 files = set()
1958 files = set()
1959 try:
1959 try:
1960 patch.patch(
1960 patch.patch(
1961 ui,
1961 ui,
1962 repo,
1962 repo,
1963 tmpname,
1963 tmpname,
1964 strip=strip,
1964 strip=strip,
1965 prefix=prefix,
1965 prefix=prefix,
1966 files=files,
1966 files=files,
1967 eolmode=None,
1967 eolmode=None,
1968 similarity=sim / 100.0,
1968 similarity=sim / 100.0,
1969 )
1969 )
1970 except error.PatchError as e:
1970 except error.PatchError as e:
1971 if not partial:
1971 if not partial:
1972 raise error.Abort(pycompat.bytestr(e))
1972 raise error.Abort(pycompat.bytestr(e))
1973 if partial:
1973 if partial:
1974 rejects = True
1974 rejects = True
1975
1975
1976 files = list(files)
1976 files = list(files)
1977 if nocommit:
1977 if nocommit:
1978 if message:
1978 if message:
1979 msgs.append(message)
1979 msgs.append(message)
1980 else:
1980 else:
1981 if opts.get(b'exact') or p2:
1981 if opts.get(b'exact') or p2:
1982 # If you got here, you either use --force and know what
1982 # If you got here, you either use --force and know what
1983 # you are doing or used --exact or a merge patch while
1983 # you are doing or used --exact or a merge patch while
1984 # being updated to its first parent.
1984 # being updated to its first parent.
1985 m = None
1985 m = None
1986 else:
1986 else:
1987 m = scmutil.matchfiles(repo, files or [])
1987 m = scmutil.matchfiles(repo, files or [])
1988 editform = mergeeditform(repo[None], b'import.normal')
1988 editform = mergeeditform(repo[None], b'import.normal')
1989 if opts.get(b'exact'):
1989 if opts.get(b'exact'):
1990 editor = None
1990 editor = None
1991 else:
1991 else:
1992 editor = getcommiteditor(
1992 editor = getcommiteditor(
1993 editform=editform, **pycompat.strkwargs(opts)
1993 editform=editform, **pycompat.strkwargs(opts)
1994 )
1994 )
1995 extra = {}
1995 extra = {}
1996 for idfunc in extrapreimport:
1996 for idfunc in extrapreimport:
1997 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1997 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1998 overrides = {}
1998 overrides = {}
1999 if partial:
1999 if partial:
2000 overrides[(b'ui', b'allowemptycommit')] = True
2000 overrides[(b'ui', b'allowemptycommit')] = True
2001 if opts.get(b'secret'):
2001 if opts.get(b'secret'):
2002 overrides[(b'phases', b'new-commit')] = b'secret'
2002 overrides[(b'phases', b'new-commit')] = b'secret'
2003 with repo.ui.configoverride(overrides, b'import'):
2003 with repo.ui.configoverride(overrides, b'import'):
2004 n = repo.commit(
2004 n = repo.commit(
2005 message, user, date, match=m, editor=editor, extra=extra
2005 message, user, date, match=m, editor=editor, extra=extra
2006 )
2006 )
2007 for idfunc in extrapostimport:
2007 for idfunc in extrapostimport:
2008 extrapostimportmap[idfunc](repo[n])
2008 extrapostimportmap[idfunc](repo[n])
2009 else:
2009 else:
2010 if opts.get(b'exact') or importbranch:
2010 if opts.get(b'exact') or importbranch:
2011 branch = branch or b'default'
2011 branch = branch or b'default'
2012 else:
2012 else:
2013 branch = p1.branch()
2013 branch = p1.branch()
2014 store = patch.filestore()
2014 store = patch.filestore()
2015 try:
2015 try:
2016 files = set()
2016 files = set()
2017 try:
2017 try:
2018 patch.patchrepo(
2018 patch.patchrepo(
2019 ui,
2019 ui,
2020 repo,
2020 repo,
2021 p1,
2021 p1,
2022 store,
2022 store,
2023 tmpname,
2023 tmpname,
2024 strip,
2024 strip,
2025 prefix,
2025 prefix,
2026 files,
2026 files,
2027 eolmode=None,
2027 eolmode=None,
2028 )
2028 )
2029 except error.PatchError as e:
2029 except error.PatchError as e:
2030 raise error.Abort(stringutil.forcebytestr(e))
2030 raise error.Abort(stringutil.forcebytestr(e))
2031 if opts.get(b'exact'):
2031 if opts.get(b'exact'):
2032 editor = None
2032 editor = None
2033 else:
2033 else:
2034 editor = getcommiteditor(editform=b'import.bypass')
2034 editor = getcommiteditor(editform=b'import.bypass')
2035 memctx = context.memctx(
2035 memctx = context.memctx(
2036 repo,
2036 repo,
2037 (p1.node(), p2.node()),
2037 (p1.node(), p2.node()),
2038 message,
2038 message,
2039 files=files,
2039 files=files,
2040 filectxfn=store,
2040 filectxfn=store,
2041 user=user,
2041 user=user,
2042 date=date,
2042 date=date,
2043 branch=branch,
2043 branch=branch,
2044 editor=editor,
2044 editor=editor,
2045 )
2045 )
2046
2046
2047 overrides = {}
2047 overrides = {}
2048 if opts.get(b'secret'):
2048 if opts.get(b'secret'):
2049 overrides[(b'phases', b'new-commit')] = b'secret'
2049 overrides[(b'phases', b'new-commit')] = b'secret'
2050 with repo.ui.configoverride(overrides, b'import'):
2050 with repo.ui.configoverride(overrides, b'import'):
2051 n = memctx.commit()
2051 n = memctx.commit()
2052 finally:
2052 finally:
2053 store.close()
2053 store.close()
2054 if opts.get(b'exact') and nocommit:
2054 if opts.get(b'exact') and nocommit:
2055 # --exact with --no-commit is still useful in that it does merge
2055 # --exact with --no-commit is still useful in that it does merge
2056 # and branch bits
2056 # and branch bits
2057 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
2057 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
2058 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
2058 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
2059 raise error.Abort(_(b'patch is damaged or loses information'))
2059 raise error.Abort(_(b'patch is damaged or loses information'))
2060 msg = _(b'applied to working directory')
2060 msg = _(b'applied to working directory')
2061 if n:
2061 if n:
2062 # i18n: refers to a short changeset id
2062 # i18n: refers to a short changeset id
2063 msg = _(b'created %s') % short(n)
2063 msg = _(b'created %s') % short(n)
2064 return msg, n, rejects
2064 return msg, n, rejects
2065
2065
2066
2066
2067 # facility to let extensions include additional data in an exported patch
2067 # facility to let extensions include additional data in an exported patch
2068 # list of identifiers to be executed in order
2068 # list of identifiers to be executed in order
2069 extraexport = []
2069 extraexport = []
2070 # mapping from identifier to actual export function
2070 # mapping from identifier to actual export function
2071 # function as to return a string to be added to the header or None
2071 # function as to return a string to be added to the header or None
2072 # it is given two arguments (sequencenumber, changectx)
2072 # it is given two arguments (sequencenumber, changectx)
2073 extraexportmap = {}
2073 extraexportmap = {}
2074
2074
2075
2075
2076 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
2076 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
2077 node = scmutil.binnode(ctx)
2077 node = scmutil.binnode(ctx)
2078 parents = [p.node() for p in ctx.parents() if p]
2078 parents = [p.node() for p in ctx.parents() if p]
2079 branch = ctx.branch()
2079 branch = ctx.branch()
2080 if switch_parent:
2080 if switch_parent:
2081 parents.reverse()
2081 parents.reverse()
2082
2082
2083 if parents:
2083 if parents:
2084 prev = parents[0]
2084 prev = parents[0]
2085 else:
2085 else:
2086 prev = nullid
2086 prev = nullid
2087
2087
2088 fm.context(ctx=ctx)
2088 fm.context(ctx=ctx)
2089 fm.plain(b'# HG changeset patch\n')
2089 fm.plain(b'# HG changeset patch\n')
2090 fm.write(b'user', b'# User %s\n', ctx.user())
2090 fm.write(b'user', b'# User %s\n', ctx.user())
2091 fm.plain(b'# Date %d %d\n' % ctx.date())
2091 fm.plain(b'# Date %d %d\n' % ctx.date())
2092 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
2092 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
2093 fm.condwrite(
2093 fm.condwrite(
2094 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
2094 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
2095 )
2095 )
2096 fm.write(b'node', b'# Node ID %s\n', hex(node))
2096 fm.write(b'node', b'# Node ID %s\n', hex(node))
2097 fm.plain(b'# Parent %s\n' % hex(prev))
2097 fm.plain(b'# Parent %s\n' % hex(prev))
2098 if len(parents) > 1:
2098 if len(parents) > 1:
2099 fm.plain(b'# Parent %s\n' % hex(parents[1]))
2099 fm.plain(b'# Parent %s\n' % hex(parents[1]))
2100 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
2100 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
2101
2101
2102 # TODO: redesign extraexportmap function to support formatter
2102 # TODO: redesign extraexportmap function to support formatter
2103 for headerid in extraexport:
2103 for headerid in extraexport:
2104 header = extraexportmap[headerid](seqno, ctx)
2104 header = extraexportmap[headerid](seqno, ctx)
2105 if header is not None:
2105 if header is not None:
2106 fm.plain(b'# %s\n' % header)
2106 fm.plain(b'# %s\n' % header)
2107
2107
2108 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
2108 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
2109 fm.plain(b'\n')
2109 fm.plain(b'\n')
2110
2110
2111 if fm.isplain():
2111 if fm.isplain():
2112 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
2112 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
2113 for chunk, label in chunkiter:
2113 for chunk, label in chunkiter:
2114 fm.plain(chunk, label=label)
2114 fm.plain(chunk, label=label)
2115 else:
2115 else:
2116 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
2116 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
2117 # TODO: make it structured?
2117 # TODO: make it structured?
2118 fm.data(diff=b''.join(chunkiter))
2118 fm.data(diff=b''.join(chunkiter))
2119
2119
2120
2120
2121 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
2121 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
2122 """Export changesets to stdout or a single file"""
2122 """Export changesets to stdout or a single file"""
2123 for seqno, rev in enumerate(revs, 1):
2123 for seqno, rev in enumerate(revs, 1):
2124 ctx = repo[rev]
2124 ctx = repo[rev]
2125 if not dest.startswith(b'<'):
2125 if not dest.startswith(b'<'):
2126 repo.ui.note(b"%s\n" % dest)
2126 repo.ui.note(b"%s\n" % dest)
2127 fm.startitem()
2127 fm.startitem()
2128 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
2128 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
2129
2129
2130
2130
2131 def _exportfntemplate(
2131 def _exportfntemplate(
2132 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
2132 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
2133 ):
2133 ):
2134 """Export changesets to possibly multiple files"""
2134 """Export changesets to possibly multiple files"""
2135 total = len(revs)
2135 total = len(revs)
2136 revwidth = max(len(str(rev)) for rev in revs)
2136 revwidth = max(len(str(rev)) for rev in revs)
2137 filemap = util.sortdict() # filename: [(seqno, rev), ...]
2137 filemap = util.sortdict() # filename: [(seqno, rev), ...]
2138
2138
2139 for seqno, rev in enumerate(revs, 1):
2139 for seqno, rev in enumerate(revs, 1):
2140 ctx = repo[rev]
2140 ctx = repo[rev]
2141 dest = makefilename(
2141 dest = makefilename(
2142 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
2142 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
2143 )
2143 )
2144 filemap.setdefault(dest, []).append((seqno, rev))
2144 filemap.setdefault(dest, []).append((seqno, rev))
2145
2145
2146 for dest in filemap:
2146 for dest in filemap:
2147 with formatter.maybereopen(basefm, dest) as fm:
2147 with formatter.maybereopen(basefm, dest) as fm:
2148 repo.ui.note(b"%s\n" % dest)
2148 repo.ui.note(b"%s\n" % dest)
2149 for seqno, rev in filemap[dest]:
2149 for seqno, rev in filemap[dest]:
2150 fm.startitem()
2150 fm.startitem()
2151 ctx = repo[rev]
2151 ctx = repo[rev]
2152 _exportsingle(
2152 _exportsingle(
2153 repo, ctx, fm, match, switch_parent, seqno, diffopts
2153 repo, ctx, fm, match, switch_parent, seqno, diffopts
2154 )
2154 )
2155
2155
2156
2156
2157 def _prefetchchangedfiles(repo, revs, match):
2157 def _prefetchchangedfiles(repo, revs, match):
2158 allfiles = set()
2158 allfiles = set()
2159 for rev in revs:
2159 for rev in revs:
2160 for file in repo[rev].files():
2160 for file in repo[rev].files():
2161 if not match or match(file):
2161 if not match or match(file):
2162 allfiles.add(file)
2162 allfiles.add(file)
2163 match = scmutil.matchfiles(repo, allfiles)
2163 match = scmutil.matchfiles(repo, allfiles)
2164 revmatches = [(rev, match) for rev in revs]
2164 revmatches = [(rev, match) for rev in revs]
2165 scmutil.prefetchfiles(repo, revmatches)
2165 scmutil.prefetchfiles(repo, revmatches)
2166
2166
2167
2167
2168 def export(
2168 def export(
2169 repo,
2169 repo,
2170 revs,
2170 revs,
2171 basefm,
2171 basefm,
2172 fntemplate=b'hg-%h.patch',
2172 fntemplate=b'hg-%h.patch',
2173 switch_parent=False,
2173 switch_parent=False,
2174 opts=None,
2174 opts=None,
2175 match=None,
2175 match=None,
2176 ):
2176 ):
2177 '''export changesets as hg patches
2177 '''export changesets as hg patches
2178
2178
2179 Args:
2179 Args:
2180 repo: The repository from which we're exporting revisions.
2180 repo: The repository from which we're exporting revisions.
2181 revs: A list of revisions to export as revision numbers.
2181 revs: A list of revisions to export as revision numbers.
2182 basefm: A formatter to which patches should be written.
2182 basefm: A formatter to which patches should be written.
2183 fntemplate: An optional string to use for generating patch file names.
2183 fntemplate: An optional string to use for generating patch file names.
2184 switch_parent: If True, show diffs against second parent when not nullid.
2184 switch_parent: If True, show diffs against second parent when not nullid.
2185 Default is false, which always shows diff against p1.
2185 Default is false, which always shows diff against p1.
2186 opts: diff options to use for generating the patch.
2186 opts: diff options to use for generating the patch.
2187 match: If specified, only export changes to files matching this matcher.
2187 match: If specified, only export changes to files matching this matcher.
2188
2188
2189 Returns:
2189 Returns:
2190 Nothing.
2190 Nothing.
2191
2191
2192 Side Effect:
2192 Side Effect:
2193 "HG Changeset Patch" data is emitted to one of the following
2193 "HG Changeset Patch" data is emitted to one of the following
2194 destinations:
2194 destinations:
2195 fntemplate specified: Each rev is written to a unique file named using
2195 fntemplate specified: Each rev is written to a unique file named using
2196 the given template.
2196 the given template.
2197 Otherwise: All revs will be written to basefm.
2197 Otherwise: All revs will be written to basefm.
2198 '''
2198 '''
2199 _prefetchchangedfiles(repo, revs, match)
2199 _prefetchchangedfiles(repo, revs, match)
2200
2200
2201 if not fntemplate:
2201 if not fntemplate:
2202 _exportfile(
2202 _exportfile(
2203 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2203 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2204 )
2204 )
2205 else:
2205 else:
2206 _exportfntemplate(
2206 _exportfntemplate(
2207 repo, revs, basefm, fntemplate, switch_parent, opts, match
2207 repo, revs, basefm, fntemplate, switch_parent, opts, match
2208 )
2208 )
2209
2209
2210
2210
2211 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2211 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2212 """Export changesets to the given file stream"""
2212 """Export changesets to the given file stream"""
2213 _prefetchchangedfiles(repo, revs, match)
2213 _prefetchchangedfiles(repo, revs, match)
2214
2214
2215 dest = getattr(fp, 'name', b'<unnamed>')
2215 dest = getattr(fp, 'name', b'<unnamed>')
2216 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2216 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2217 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2217 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2218
2218
2219
2219
2220 def showmarker(fm, marker, index=None):
2220 def showmarker(fm, marker, index=None):
2221 """utility function to display obsolescence marker in a readable way
2221 """utility function to display obsolescence marker in a readable way
2222
2222
2223 To be used by debug function."""
2223 To be used by debug function."""
2224 if index is not None:
2224 if index is not None:
2225 fm.write(b'index', b'%i ', index)
2225 fm.write(b'index', b'%i ', index)
2226 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2226 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2227 succs = marker.succnodes()
2227 succs = marker.succnodes()
2228 fm.condwrite(
2228 fm.condwrite(
2229 succs,
2229 succs,
2230 b'succnodes',
2230 b'succnodes',
2231 b'%s ',
2231 b'%s ',
2232 fm.formatlist(map(hex, succs), name=b'node'),
2232 fm.formatlist(map(hex, succs), name=b'node'),
2233 )
2233 )
2234 fm.write(b'flag', b'%X ', marker.flags())
2234 fm.write(b'flag', b'%X ', marker.flags())
2235 parents = marker.parentnodes()
2235 parents = marker.parentnodes()
2236 if parents is not None:
2236 if parents is not None:
2237 fm.write(
2237 fm.write(
2238 b'parentnodes',
2238 b'parentnodes',
2239 b'{%s} ',
2239 b'{%s} ',
2240 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2240 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2241 )
2241 )
2242 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2242 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2243 meta = marker.metadata().copy()
2243 meta = marker.metadata().copy()
2244 meta.pop(b'date', None)
2244 meta.pop(b'date', None)
2245 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2245 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2246 fm.write(
2246 fm.write(
2247 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2247 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2248 )
2248 )
2249 fm.plain(b'\n')
2249 fm.plain(b'\n')
2250
2250
2251
2251
2252 def finddate(ui, repo, date):
2252 def finddate(ui, repo, date):
2253 """Find the tipmost changeset that matches the given date spec"""
2253 """Find the tipmost changeset that matches the given date spec"""
2254 mrevs = repo.revs(b'date(%s)', date)
2254 mrevs = repo.revs(b'date(%s)', date)
2255 try:
2255 try:
2256 rev = mrevs.max()
2256 rev = mrevs.max()
2257 except ValueError:
2257 except ValueError:
2258 raise error.Abort(_(b"revision matching date not found"))
2258 raise error.Abort(_(b"revision matching date not found"))
2259
2259
2260 ui.status(
2260 ui.status(
2261 _(b"found revision %d from %s\n")
2261 _(b"found revision %d from %s\n")
2262 % (rev, dateutil.datestr(repo[rev].date()))
2262 % (rev, dateutil.datestr(repo[rev].date()))
2263 )
2263 )
2264 return b'%d' % rev
2264 return b'%d' % rev
2265
2265
2266
2266
2267 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2267 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2268 bad = []
2268 bad = []
2269
2269
2270 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2270 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2271 names = []
2271 names = []
2272 wctx = repo[None]
2272 wctx = repo[None]
2273 cca = None
2273 cca = None
2274 abort, warn = scmutil.checkportabilityalert(ui)
2274 abort, warn = scmutil.checkportabilityalert(ui)
2275 if abort or warn:
2275 if abort or warn:
2276 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2276 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2277
2277
2278 match = repo.narrowmatch(match, includeexact=True)
2278 match = repo.narrowmatch(match, includeexact=True)
2279 badmatch = matchmod.badmatch(match, badfn)
2279 badmatch = matchmod.badmatch(match, badfn)
2280 dirstate = repo.dirstate
2280 dirstate = repo.dirstate
2281 # We don't want to just call wctx.walk here, since it would return a lot of
2281 # We don't want to just call wctx.walk here, since it would return a lot of
2282 # clean files, which we aren't interested in and takes time.
2282 # clean files, which we aren't interested in and takes time.
2283 for f in sorted(
2283 for f in sorted(
2284 dirstate.walk(
2284 dirstate.walk(
2285 badmatch,
2285 badmatch,
2286 subrepos=sorted(wctx.substate),
2286 subrepos=sorted(wctx.substate),
2287 unknown=True,
2287 unknown=True,
2288 ignored=False,
2288 ignored=False,
2289 full=False,
2289 full=False,
2290 )
2290 )
2291 ):
2291 ):
2292 exact = match.exact(f)
2292 exact = match.exact(f)
2293 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2293 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2294 if cca:
2294 if cca:
2295 cca(f)
2295 cca(f)
2296 names.append(f)
2296 names.append(f)
2297 if ui.verbose or not exact:
2297 if ui.verbose or not exact:
2298 ui.status(
2298 ui.status(
2299 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2299 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2300 )
2300 )
2301
2301
2302 for subpath in sorted(wctx.substate):
2302 for subpath in sorted(wctx.substate):
2303 sub = wctx.sub(subpath)
2303 sub = wctx.sub(subpath)
2304 try:
2304 try:
2305 submatch = matchmod.subdirmatcher(subpath, match)
2305 submatch = matchmod.subdirmatcher(subpath, match)
2306 subprefix = repo.wvfs.reljoin(prefix, subpath)
2306 subprefix = repo.wvfs.reljoin(prefix, subpath)
2307 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2307 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2308 if opts.get('subrepos'):
2308 if opts.get('subrepos'):
2309 bad.extend(
2309 bad.extend(
2310 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2310 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2311 )
2311 )
2312 else:
2312 else:
2313 bad.extend(
2313 bad.extend(
2314 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2314 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2315 )
2315 )
2316 except error.LookupError:
2316 except error.LookupError:
2317 ui.status(
2317 ui.status(
2318 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2318 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2319 )
2319 )
2320
2320
2321 if not opts.get('dry_run'):
2321 if not opts.get('dry_run'):
2322 rejected = wctx.add(names, prefix)
2322 rejected = wctx.add(names, prefix)
2323 bad.extend(f for f in rejected if f in match.files())
2323 bad.extend(f for f in rejected if f in match.files())
2324 return bad
2324 return bad
2325
2325
2326
2326
2327 def addwebdirpath(repo, serverpath, webconf):
2327 def addwebdirpath(repo, serverpath, webconf):
2328 webconf[serverpath] = repo.root
2328 webconf[serverpath] = repo.root
2329 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2329 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2330
2330
2331 for r in repo.revs(b'filelog("path:.hgsub")'):
2331 for r in repo.revs(b'filelog("path:.hgsub")'):
2332 ctx = repo[r]
2332 ctx = repo[r]
2333 for subpath in ctx.substate:
2333 for subpath in ctx.substate:
2334 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2334 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2335
2335
2336
2336
2337 def forget(
2337 def forget(
2338 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2338 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2339 ):
2339 ):
2340 if dryrun and interactive:
2340 if dryrun and interactive:
2341 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2341 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2342 bad = []
2342 bad = []
2343 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2343 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2344 wctx = repo[None]
2344 wctx = repo[None]
2345 forgot = []
2345 forgot = []
2346
2346
2347 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2347 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2348 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2348 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2349 if explicitonly:
2349 if explicitonly:
2350 forget = [f for f in forget if match.exact(f)]
2350 forget = [f for f in forget if match.exact(f)]
2351
2351
2352 for subpath in sorted(wctx.substate):
2352 for subpath in sorted(wctx.substate):
2353 sub = wctx.sub(subpath)
2353 sub = wctx.sub(subpath)
2354 submatch = matchmod.subdirmatcher(subpath, match)
2354 submatch = matchmod.subdirmatcher(subpath, match)
2355 subprefix = repo.wvfs.reljoin(prefix, subpath)
2355 subprefix = repo.wvfs.reljoin(prefix, subpath)
2356 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2356 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2357 try:
2357 try:
2358 subbad, subforgot = sub.forget(
2358 subbad, subforgot = sub.forget(
2359 submatch,
2359 submatch,
2360 subprefix,
2360 subprefix,
2361 subuipathfn,
2361 subuipathfn,
2362 dryrun=dryrun,
2362 dryrun=dryrun,
2363 interactive=interactive,
2363 interactive=interactive,
2364 )
2364 )
2365 bad.extend([subpath + b'/' + f for f in subbad])
2365 bad.extend([subpath + b'/' + f for f in subbad])
2366 forgot.extend([subpath + b'/' + f for f in subforgot])
2366 forgot.extend([subpath + b'/' + f for f in subforgot])
2367 except error.LookupError:
2367 except error.LookupError:
2368 ui.status(
2368 ui.status(
2369 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2369 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2370 )
2370 )
2371
2371
2372 if not explicitonly:
2372 if not explicitonly:
2373 for f in match.files():
2373 for f in match.files():
2374 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2374 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2375 if f not in forgot:
2375 if f not in forgot:
2376 if repo.wvfs.exists(f):
2376 if repo.wvfs.exists(f):
2377 # Don't complain if the exact case match wasn't given.
2377 # Don't complain if the exact case match wasn't given.
2378 # But don't do this until after checking 'forgot', so
2378 # But don't do this until after checking 'forgot', so
2379 # that subrepo files aren't normalized, and this op is
2379 # that subrepo files aren't normalized, and this op is
2380 # purely from data cached by the status walk above.
2380 # purely from data cached by the status walk above.
2381 if repo.dirstate.normalize(f) in repo.dirstate:
2381 if repo.dirstate.normalize(f) in repo.dirstate:
2382 continue
2382 continue
2383 ui.warn(
2383 ui.warn(
2384 _(
2384 _(
2385 b'not removing %s: '
2385 b'not removing %s: '
2386 b'file is already untracked\n'
2386 b'file is already untracked\n'
2387 )
2387 )
2388 % uipathfn(f)
2388 % uipathfn(f)
2389 )
2389 )
2390 bad.append(f)
2390 bad.append(f)
2391
2391
2392 if interactive:
2392 if interactive:
2393 responses = _(
2393 responses = _(
2394 b'[Ynsa?]'
2394 b'[Ynsa?]'
2395 b'$$ &Yes, forget this file'
2395 b'$$ &Yes, forget this file'
2396 b'$$ &No, skip this file'
2396 b'$$ &No, skip this file'
2397 b'$$ &Skip remaining files'
2397 b'$$ &Skip remaining files'
2398 b'$$ Include &all remaining files'
2398 b'$$ Include &all remaining files'
2399 b'$$ &? (display help)'
2399 b'$$ &? (display help)'
2400 )
2400 )
2401 for filename in forget[:]:
2401 for filename in forget[:]:
2402 r = ui.promptchoice(
2402 r = ui.promptchoice(
2403 _(b'forget %s %s') % (uipathfn(filename), responses)
2403 _(b'forget %s %s') % (uipathfn(filename), responses)
2404 )
2404 )
2405 if r == 4: # ?
2405 if r == 4: # ?
2406 while r == 4:
2406 while r == 4:
2407 for c, t in ui.extractchoices(responses)[1]:
2407 for c, t in ui.extractchoices(responses)[1]:
2408 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2408 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2409 r = ui.promptchoice(
2409 r = ui.promptchoice(
2410 _(b'forget %s %s') % (uipathfn(filename), responses)
2410 _(b'forget %s %s') % (uipathfn(filename), responses)
2411 )
2411 )
2412 if r == 0: # yes
2412 if r == 0: # yes
2413 continue
2413 continue
2414 elif r == 1: # no
2414 elif r == 1: # no
2415 forget.remove(filename)
2415 forget.remove(filename)
2416 elif r == 2: # Skip
2416 elif r == 2: # Skip
2417 fnindex = forget.index(filename)
2417 fnindex = forget.index(filename)
2418 del forget[fnindex:]
2418 del forget[fnindex:]
2419 break
2419 break
2420 elif r == 3: # All
2420 elif r == 3: # All
2421 break
2421 break
2422
2422
2423 for f in forget:
2423 for f in forget:
2424 if ui.verbose or not match.exact(f) or interactive:
2424 if ui.verbose or not match.exact(f) or interactive:
2425 ui.status(
2425 ui.status(
2426 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2426 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2427 )
2427 )
2428
2428
2429 if not dryrun:
2429 if not dryrun:
2430 rejected = wctx.forget(forget, prefix)
2430 rejected = wctx.forget(forget, prefix)
2431 bad.extend(f for f in rejected if f in match.files())
2431 bad.extend(f for f in rejected if f in match.files())
2432 forgot.extend(f for f in forget if f not in rejected)
2432 forgot.extend(f for f in forget if f not in rejected)
2433 return bad, forgot
2433 return bad, forgot
2434
2434
2435
2435
2436 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2436 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2437 ret = 1
2437 ret = 1
2438
2438
2439 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2439 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2440 if fm.isplain() and not needsfctx:
2440 if fm.isplain() and not needsfctx:
2441 # Fast path. The speed-up comes from skipping the formatter, and batching
2441 # Fast path. The speed-up comes from skipping the formatter, and batching
2442 # calls to ui.write.
2442 # calls to ui.write.
2443 buf = []
2443 buf = []
2444 for f in ctx.matches(m):
2444 for f in ctx.matches(m):
2445 buf.append(fmt % uipathfn(f))
2445 buf.append(fmt % uipathfn(f))
2446 if len(buf) > 100:
2446 if len(buf) > 100:
2447 ui.write(b''.join(buf))
2447 ui.write(b''.join(buf))
2448 del buf[:]
2448 del buf[:]
2449 ret = 0
2449 ret = 0
2450 if buf:
2450 if buf:
2451 ui.write(b''.join(buf))
2451 ui.write(b''.join(buf))
2452 else:
2452 else:
2453 for f in ctx.matches(m):
2453 for f in ctx.matches(m):
2454 fm.startitem()
2454 fm.startitem()
2455 fm.context(ctx=ctx)
2455 fm.context(ctx=ctx)
2456 if needsfctx:
2456 if needsfctx:
2457 fc = ctx[f]
2457 fc = ctx[f]
2458 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2458 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2459 fm.data(path=f)
2459 fm.data(path=f)
2460 fm.plain(fmt % uipathfn(f))
2460 fm.plain(fmt % uipathfn(f))
2461 ret = 0
2461 ret = 0
2462
2462
2463 for subpath in sorted(ctx.substate):
2463 for subpath in sorted(ctx.substate):
2464 submatch = matchmod.subdirmatcher(subpath, m)
2464 submatch = matchmod.subdirmatcher(subpath, m)
2465 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2465 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2466 if subrepos or m.exact(subpath) or any(submatch.files()):
2466 if subrepos or m.exact(subpath) or any(submatch.files()):
2467 sub = ctx.sub(subpath)
2467 sub = ctx.sub(subpath)
2468 try:
2468 try:
2469 recurse = m.exact(subpath) or subrepos
2469 recurse = m.exact(subpath) or subrepos
2470 if (
2470 if (
2471 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2471 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2472 == 0
2472 == 0
2473 ):
2473 ):
2474 ret = 0
2474 ret = 0
2475 except error.LookupError:
2475 except error.LookupError:
2476 ui.status(
2476 ui.status(
2477 _(b"skipping missing subrepository: %s\n")
2477 _(b"skipping missing subrepository: %s\n")
2478 % uipathfn(subpath)
2478 % uipathfn(subpath)
2479 )
2479 )
2480
2480
2481 return ret
2481 return ret
2482
2482
2483
2483
2484 def remove(
2484 def remove(
2485 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2485 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2486 ):
2486 ):
2487 ret = 0
2487 ret = 0
2488 s = repo.status(match=m, clean=True)
2488 s = repo.status(match=m, clean=True)
2489 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2489 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2490
2490
2491 wctx = repo[None]
2491 wctx = repo[None]
2492
2492
2493 if warnings is None:
2493 if warnings is None:
2494 warnings = []
2494 warnings = []
2495 warn = True
2495 warn = True
2496 else:
2496 else:
2497 warn = False
2497 warn = False
2498
2498
2499 subs = sorted(wctx.substate)
2499 subs = sorted(wctx.substate)
2500 progress = ui.makeprogress(
2500 progress = ui.makeprogress(
2501 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2501 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2502 )
2502 )
2503 for subpath in subs:
2503 for subpath in subs:
2504 submatch = matchmod.subdirmatcher(subpath, m)
2504 submatch = matchmod.subdirmatcher(subpath, m)
2505 subprefix = repo.wvfs.reljoin(prefix, subpath)
2505 subprefix = repo.wvfs.reljoin(prefix, subpath)
2506 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2506 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2507 if subrepos or m.exact(subpath) or any(submatch.files()):
2507 if subrepos or m.exact(subpath) or any(submatch.files()):
2508 progress.increment()
2508 progress.increment()
2509 sub = wctx.sub(subpath)
2509 sub = wctx.sub(subpath)
2510 try:
2510 try:
2511 if sub.removefiles(
2511 if sub.removefiles(
2512 submatch,
2512 submatch,
2513 subprefix,
2513 subprefix,
2514 subuipathfn,
2514 subuipathfn,
2515 after,
2515 after,
2516 force,
2516 force,
2517 subrepos,
2517 subrepos,
2518 dryrun,
2518 dryrun,
2519 warnings,
2519 warnings,
2520 ):
2520 ):
2521 ret = 1
2521 ret = 1
2522 except error.LookupError:
2522 except error.LookupError:
2523 warnings.append(
2523 warnings.append(
2524 _(b"skipping missing subrepository: %s\n")
2524 _(b"skipping missing subrepository: %s\n")
2525 % uipathfn(subpath)
2525 % uipathfn(subpath)
2526 )
2526 )
2527 progress.complete()
2527 progress.complete()
2528
2528
2529 # warn about failure to delete explicit files/dirs
2529 # warn about failure to delete explicit files/dirs
2530 deleteddirs = pathutil.dirs(deleted)
2530 deleteddirs = pathutil.dirs(deleted)
2531 files = m.files()
2531 files = m.files()
2532 progress = ui.makeprogress(
2532 progress = ui.makeprogress(
2533 _(b'deleting'), total=len(files), unit=_(b'files')
2533 _(b'deleting'), total=len(files), unit=_(b'files')
2534 )
2534 )
2535 for f in files:
2535 for f in files:
2536
2536
2537 def insubrepo():
2537 def insubrepo():
2538 for subpath in wctx.substate:
2538 for subpath in wctx.substate:
2539 if f.startswith(subpath + b'/'):
2539 if f.startswith(subpath + b'/'):
2540 return True
2540 return True
2541 return False
2541 return False
2542
2542
2543 progress.increment()
2543 progress.increment()
2544 isdir = f in deleteddirs or wctx.hasdir(f)
2544 isdir = f in deleteddirs or wctx.hasdir(f)
2545 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2545 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2546 continue
2546 continue
2547
2547
2548 if repo.wvfs.exists(f):
2548 if repo.wvfs.exists(f):
2549 if repo.wvfs.isdir(f):
2549 if repo.wvfs.isdir(f):
2550 warnings.append(
2550 warnings.append(
2551 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2551 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2552 )
2552 )
2553 else:
2553 else:
2554 warnings.append(
2554 warnings.append(
2555 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2555 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2556 )
2556 )
2557 # missing files will generate a warning elsewhere
2557 # missing files will generate a warning elsewhere
2558 ret = 1
2558 ret = 1
2559 progress.complete()
2559 progress.complete()
2560
2560
2561 if force:
2561 if force:
2562 list = modified + deleted + clean + added
2562 list = modified + deleted + clean + added
2563 elif after:
2563 elif after:
2564 list = deleted
2564 list = deleted
2565 remaining = modified + added + clean
2565 remaining = modified + added + clean
2566 progress = ui.makeprogress(
2566 progress = ui.makeprogress(
2567 _(b'skipping'), total=len(remaining), unit=_(b'files')
2567 _(b'skipping'), total=len(remaining), unit=_(b'files')
2568 )
2568 )
2569 for f in remaining:
2569 for f in remaining:
2570 progress.increment()
2570 progress.increment()
2571 if ui.verbose or (f in files):
2571 if ui.verbose or (f in files):
2572 warnings.append(
2572 warnings.append(
2573 _(b'not removing %s: file still exists\n') % uipathfn(f)
2573 _(b'not removing %s: file still exists\n') % uipathfn(f)
2574 )
2574 )
2575 ret = 1
2575 ret = 1
2576 progress.complete()
2576 progress.complete()
2577 else:
2577 else:
2578 list = deleted + clean
2578 list = deleted + clean
2579 progress = ui.makeprogress(
2579 progress = ui.makeprogress(
2580 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2580 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2581 )
2581 )
2582 for f in modified:
2582 for f in modified:
2583 progress.increment()
2583 progress.increment()
2584 warnings.append(
2584 warnings.append(
2585 _(
2585 _(
2586 b'not removing %s: file is modified (use -f'
2586 b'not removing %s: file is modified (use -f'
2587 b' to force removal)\n'
2587 b' to force removal)\n'
2588 )
2588 )
2589 % uipathfn(f)
2589 % uipathfn(f)
2590 )
2590 )
2591 ret = 1
2591 ret = 1
2592 for f in added:
2592 for f in added:
2593 progress.increment()
2593 progress.increment()
2594 warnings.append(
2594 warnings.append(
2595 _(
2595 _(
2596 b"not removing %s: file has been marked for add"
2596 b"not removing %s: file has been marked for add"
2597 b" (use 'hg forget' to undo add)\n"
2597 b" (use 'hg forget' to undo add)\n"
2598 )
2598 )
2599 % uipathfn(f)
2599 % uipathfn(f)
2600 )
2600 )
2601 ret = 1
2601 ret = 1
2602 progress.complete()
2602 progress.complete()
2603
2603
2604 list = sorted(list)
2604 list = sorted(list)
2605 progress = ui.makeprogress(
2605 progress = ui.makeprogress(
2606 _(b'deleting'), total=len(list), unit=_(b'files')
2606 _(b'deleting'), total=len(list), unit=_(b'files')
2607 )
2607 )
2608 for f in list:
2608 for f in list:
2609 if ui.verbose or not m.exact(f):
2609 if ui.verbose or not m.exact(f):
2610 progress.increment()
2610 progress.increment()
2611 ui.status(
2611 ui.status(
2612 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2612 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2613 )
2613 )
2614 progress.complete()
2614 progress.complete()
2615
2615
2616 if not dryrun:
2616 if not dryrun:
2617 with repo.wlock():
2617 with repo.wlock():
2618 if not after:
2618 if not after:
2619 for f in list:
2619 for f in list:
2620 if f in added:
2620 if f in added:
2621 continue # we never unlink added files on remove
2621 continue # we never unlink added files on remove
2622 rmdir = repo.ui.configbool(
2622 rmdir = repo.ui.configbool(
2623 b'experimental', b'removeemptydirs'
2623 b'experimental', b'removeemptydirs'
2624 )
2624 )
2625 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2625 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2626 repo[None].forget(list)
2626 repo[None].forget(list)
2627
2627
2628 if warn:
2628 if warn:
2629 for warning in warnings:
2629 for warning in warnings:
2630 ui.warn(warning)
2630 ui.warn(warning)
2631
2631
2632 return ret
2632 return ret
2633
2633
2634
2634
2635 def _catfmtneedsdata(fm):
2635 def _catfmtneedsdata(fm):
2636 return not fm.datahint() or b'data' in fm.datahint()
2636 return not fm.datahint() or b'data' in fm.datahint()
2637
2637
2638
2638
2639 def _updatecatformatter(fm, ctx, matcher, path, decode):
2639 def _updatecatformatter(fm, ctx, matcher, path, decode):
2640 """Hook for adding data to the formatter used by ``hg cat``.
2640 """Hook for adding data to the formatter used by ``hg cat``.
2641
2641
2642 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2642 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2643 this method first."""
2643 this method first."""
2644
2644
2645 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2645 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2646 # wasn't requested.
2646 # wasn't requested.
2647 data = b''
2647 data = b''
2648 if _catfmtneedsdata(fm):
2648 if _catfmtneedsdata(fm):
2649 data = ctx[path].data()
2649 data = ctx[path].data()
2650 if decode:
2650 if decode:
2651 data = ctx.repo().wwritedata(path, data)
2651 data = ctx.repo().wwritedata(path, data)
2652 fm.startitem()
2652 fm.startitem()
2653 fm.context(ctx=ctx)
2653 fm.context(ctx=ctx)
2654 fm.write(b'data', b'%s', data)
2654 fm.write(b'data', b'%s', data)
2655 fm.data(path=path)
2655 fm.data(path=path)
2656
2656
2657
2657
2658 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2658 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2659 err = 1
2659 err = 1
2660 opts = pycompat.byteskwargs(opts)
2660 opts = pycompat.byteskwargs(opts)
2661
2661
2662 def write(path):
2662 def write(path):
2663 filename = None
2663 filename = None
2664 if fntemplate:
2664 if fntemplate:
2665 filename = makefilename(
2665 filename = makefilename(
2666 ctx, fntemplate, pathname=os.path.join(prefix, path)
2666 ctx, fntemplate, pathname=os.path.join(prefix, path)
2667 )
2667 )
2668 # attempt to create the directory if it does not already exist
2668 # attempt to create the directory if it does not already exist
2669 try:
2669 try:
2670 os.makedirs(os.path.dirname(filename))
2670 os.makedirs(os.path.dirname(filename))
2671 except OSError:
2671 except OSError:
2672 pass
2672 pass
2673 with formatter.maybereopen(basefm, filename) as fm:
2673 with formatter.maybereopen(basefm, filename) as fm:
2674 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2674 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2675
2675
2676 # Automation often uses hg cat on single files, so special case it
2676 # Automation often uses hg cat on single files, so special case it
2677 # for performance to avoid the cost of parsing the manifest.
2677 # for performance to avoid the cost of parsing the manifest.
2678 if len(matcher.files()) == 1 and not matcher.anypats():
2678 if len(matcher.files()) == 1 and not matcher.anypats():
2679 file = matcher.files()[0]
2679 file = matcher.files()[0]
2680 mfl = repo.manifestlog
2680 mfl = repo.manifestlog
2681 mfnode = ctx.manifestnode()
2681 mfnode = ctx.manifestnode()
2682 try:
2682 try:
2683 if mfnode and mfl[mfnode].find(file)[0]:
2683 if mfnode and mfl[mfnode].find(file)[0]:
2684 if _catfmtneedsdata(basefm):
2684 if _catfmtneedsdata(basefm):
2685 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2685 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2686 write(file)
2686 write(file)
2687 return 0
2687 return 0
2688 except KeyError:
2688 except KeyError:
2689 pass
2689 pass
2690
2690
2691 if _catfmtneedsdata(basefm):
2691 if _catfmtneedsdata(basefm):
2692 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2692 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2693
2693
2694 for abs in ctx.walk(matcher):
2694 for abs in ctx.walk(matcher):
2695 write(abs)
2695 write(abs)
2696 err = 0
2696 err = 0
2697
2697
2698 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2698 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2699 for subpath in sorted(ctx.substate):
2699 for subpath in sorted(ctx.substate):
2700 sub = ctx.sub(subpath)
2700 sub = ctx.sub(subpath)
2701 try:
2701 try:
2702 submatch = matchmod.subdirmatcher(subpath, matcher)
2702 submatch = matchmod.subdirmatcher(subpath, matcher)
2703 subprefix = os.path.join(prefix, subpath)
2703 subprefix = os.path.join(prefix, subpath)
2704 if not sub.cat(
2704 if not sub.cat(
2705 submatch,
2705 submatch,
2706 basefm,
2706 basefm,
2707 fntemplate,
2707 fntemplate,
2708 subprefix,
2708 subprefix,
2709 **pycompat.strkwargs(opts)
2709 **pycompat.strkwargs(opts)
2710 ):
2710 ):
2711 err = 0
2711 err = 0
2712 except error.RepoLookupError:
2712 except error.RepoLookupError:
2713 ui.status(
2713 ui.status(
2714 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2714 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2715 )
2715 )
2716
2716
2717 return err
2717 return err
2718
2718
2719
2719
2720 def commit(ui, repo, commitfunc, pats, opts):
2720 def commit(ui, repo, commitfunc, pats, opts):
2721 '''commit the specified files or all outstanding changes'''
2721 '''commit the specified files or all outstanding changes'''
2722 date = opts.get(b'date')
2722 date = opts.get(b'date')
2723 if date:
2723 if date:
2724 opts[b'date'] = dateutil.parsedate(date)
2724 opts[b'date'] = dateutil.parsedate(date)
2725 message = logmessage(ui, opts)
2725 message = logmessage(ui, opts)
2726 matcher = scmutil.match(repo[None], pats, opts)
2726 matcher = scmutil.match(repo[None], pats, opts)
2727
2727
2728 dsguard = None
2728 dsguard = None
2729 # extract addremove carefully -- this function can be called from a command
2729 # extract addremove carefully -- this function can be called from a command
2730 # that doesn't support addremove
2730 # that doesn't support addremove
2731 if opts.get(b'addremove'):
2731 if opts.get(b'addremove'):
2732 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2732 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2733 with dsguard or util.nullcontextmanager():
2733 with dsguard or util.nullcontextmanager():
2734 if dsguard:
2734 if dsguard:
2735 relative = scmutil.anypats(pats, opts)
2735 relative = scmutil.anypats(pats, opts)
2736 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2736 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2737 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2737 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2738 raise error.Abort(
2738 raise error.Abort(
2739 _(b"failed to mark all new/missing files as added/removed")
2739 _(b"failed to mark all new/missing files as added/removed")
2740 )
2740 )
2741
2741
2742 return commitfunc(ui, repo, message, matcher, opts)
2742 return commitfunc(ui, repo, message, matcher, opts)
2743
2743
2744
2744
2745 def samefile(f, ctx1, ctx2):
2745 def samefile(f, ctx1, ctx2):
2746 if f in ctx1.manifest():
2746 if f in ctx1.manifest():
2747 a = ctx1.filectx(f)
2747 a = ctx1.filectx(f)
2748 if f in ctx2.manifest():
2748 if f in ctx2.manifest():
2749 b = ctx2.filectx(f)
2749 b = ctx2.filectx(f)
2750 return not a.cmp(b) and a.flags() == b.flags()
2750 return not a.cmp(b) and a.flags() == b.flags()
2751 else:
2751 else:
2752 return False
2752 return False
2753 else:
2753 else:
2754 return f not in ctx2.manifest()
2754 return f not in ctx2.manifest()
2755
2755
2756
2756
2757 def amend(ui, repo, old, extra, pats, opts):
2757 def amend(ui, repo, old, extra, pats, opts):
2758 # avoid cycle context -> subrepo -> cmdutil
2758 # avoid cycle context -> subrepo -> cmdutil
2759 from . import context
2759 from . import context
2760
2760
2761 # amend will reuse the existing user if not specified, but the obsolete
2761 # amend will reuse the existing user if not specified, but the obsolete
2762 # marker creation requires that the current user's name is specified.
2762 # marker creation requires that the current user's name is specified.
2763 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2763 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2764 ui.username() # raise exception if username not set
2764 ui.username() # raise exception if username not set
2765
2765
2766 ui.note(_(b'amending changeset %s\n') % old)
2766 ui.note(_(b'amending changeset %s\n') % old)
2767 base = old.p1()
2767 base = old.p1()
2768
2768
2769 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2769 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2770 # Participating changesets:
2770 # Participating changesets:
2771 #
2771 #
2772 # wctx o - workingctx that contains changes from working copy
2772 # wctx o - workingctx that contains changes from working copy
2773 # | to go into amending commit
2773 # | to go into amending commit
2774 # |
2774 # |
2775 # old o - changeset to amend
2775 # old o - changeset to amend
2776 # |
2776 # |
2777 # base o - first parent of the changeset to amend
2777 # base o - first parent of the changeset to amend
2778 wctx = repo[None]
2778 wctx = repo[None]
2779
2779
2780 # Copy to avoid mutating input
2780 # Copy to avoid mutating input
2781 extra = extra.copy()
2781 extra = extra.copy()
2782 # Update extra dict from amended commit (e.g. to preserve graft
2782 # Update extra dict from amended commit (e.g. to preserve graft
2783 # source)
2783 # source)
2784 extra.update(old.extra())
2784 extra.update(old.extra())
2785
2785
2786 # Also update it from the from the wctx
2786 # Also update it from the from the wctx
2787 extra.update(wctx.extra())
2787 extra.update(wctx.extra())
2788
2788
2789 # date-only change should be ignored?
2789 # date-only change should be ignored?
2790 datemaydiffer = resolvecommitoptions(ui, opts)
2790 datemaydiffer = resolvecommitoptions(ui, opts)
2791
2791
2792 date = old.date()
2792 date = old.date()
2793 if opts.get(b'date'):
2793 if opts.get(b'date'):
2794 date = dateutil.parsedate(opts.get(b'date'))
2794 date = dateutil.parsedate(opts.get(b'date'))
2795 user = opts.get(b'user') or old.user()
2795 user = opts.get(b'user') or old.user()
2796
2796
2797 if len(old.parents()) > 1:
2797 if len(old.parents()) > 1:
2798 # ctx.files() isn't reliable for merges, so fall back to the
2798 # ctx.files() isn't reliable for merges, so fall back to the
2799 # slower repo.status() method
2799 # slower repo.status() method
2800 st = base.status(old)
2800 st = base.status(old)
2801 files = set(st.modified) | set(st.added) | set(st.removed)
2801 files = set(st.modified) | set(st.added) | set(st.removed)
2802 else:
2802 else:
2803 files = set(old.files())
2803 files = set(old.files())
2804
2804
2805 # add/remove the files to the working copy if the "addremove" option
2805 # add/remove the files to the working copy if the "addremove" option
2806 # was specified.
2806 # was specified.
2807 matcher = scmutil.match(wctx, pats, opts)
2807 matcher = scmutil.match(wctx, pats, opts)
2808 relative = scmutil.anypats(pats, opts)
2808 relative = scmutil.anypats(pats, opts)
2809 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2809 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2810 if opts.get(b'addremove') and scmutil.addremove(
2810 if opts.get(b'addremove') and scmutil.addremove(
2811 repo, matcher, b"", uipathfn, opts
2811 repo, matcher, b"", uipathfn, opts
2812 ):
2812 ):
2813 raise error.Abort(
2813 raise error.Abort(
2814 _(b"failed to mark all new/missing files as added/removed")
2814 _(b"failed to mark all new/missing files as added/removed")
2815 )
2815 )
2816
2816
2817 # Check subrepos. This depends on in-place wctx._status update in
2817 # Check subrepos. This depends on in-place wctx._status update in
2818 # subrepo.precommit(). To minimize the risk of this hack, we do
2818 # subrepo.precommit(). To minimize the risk of this hack, we do
2819 # nothing if .hgsub does not exist.
2819 # nothing if .hgsub does not exist.
2820 if b'.hgsub' in wctx or b'.hgsub' in old:
2820 if b'.hgsub' in wctx or b'.hgsub' in old:
2821 subs, commitsubs, newsubstate = subrepoutil.precommit(
2821 subs, commitsubs, newsubstate = subrepoutil.precommit(
2822 ui, wctx, wctx._status, matcher
2822 ui, wctx, wctx._status, matcher
2823 )
2823 )
2824 # amend should abort if commitsubrepos is enabled
2824 # amend should abort if commitsubrepos is enabled
2825 assert not commitsubs
2825 assert not commitsubs
2826 if subs:
2826 if subs:
2827 subrepoutil.writestate(repo, newsubstate)
2827 subrepoutil.writestate(repo, newsubstate)
2828
2828
2829 ms = mergestatemod.mergestate.read(repo)
2829 ms = mergestatemod.mergestate.read(repo)
2830 mergeutil.checkunresolved(ms)
2830 mergeutil.checkunresolved(ms)
2831
2831
2832 filestoamend = {f for f in wctx.files() if matcher(f)}
2832 filestoamend = {f for f in wctx.files() if matcher(f)}
2833
2833
2834 changes = len(filestoamend) > 0
2834 changes = len(filestoamend) > 0
2835 if changes:
2835 if changes:
2836 # Recompute copies (avoid recording a -> b -> a)
2836 # Recompute copies (avoid recording a -> b -> a)
2837 copied = copies.pathcopies(base, wctx, matcher)
2837 copied = copies.pathcopies(base, wctx, matcher)
2838 if old.p2:
2838 if old.p2:
2839 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2839 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2840
2840
2841 # Prune files which were reverted by the updates: if old
2841 # Prune files which were reverted by the updates: if old
2842 # introduced file X and the file was renamed in the working
2842 # introduced file X and the file was renamed in the working
2843 # copy, then those two files are the same and
2843 # copy, then those two files are the same and
2844 # we can discard X from our list of files. Likewise if X
2844 # we can discard X from our list of files. Likewise if X
2845 # was removed, it's no longer relevant. If X is missing (aka
2845 # was removed, it's no longer relevant. If X is missing (aka
2846 # deleted), old X must be preserved.
2846 # deleted), old X must be preserved.
2847 files.update(filestoamend)
2847 files.update(filestoamend)
2848 files = [
2848 files = [
2849 f
2849 f
2850 for f in files
2850 for f in files
2851 if (f not in filestoamend or not samefile(f, wctx, base))
2851 if (f not in filestoamend or not samefile(f, wctx, base))
2852 ]
2852 ]
2853
2853
2854 def filectxfn(repo, ctx_, path):
2854 def filectxfn(repo, ctx_, path):
2855 try:
2855 try:
2856 # If the file being considered is not amongst the files
2856 # If the file being considered is not amongst the files
2857 # to be amended, we should return the file context from the
2857 # to be amended, we should return the file context from the
2858 # old changeset. This avoids issues when only some files in
2858 # old changeset. This avoids issues when only some files in
2859 # the working copy are being amended but there are also
2859 # the working copy are being amended but there are also
2860 # changes to other files from the old changeset.
2860 # changes to other files from the old changeset.
2861 if path not in filestoamend:
2861 if path not in filestoamend:
2862 return old.filectx(path)
2862 return old.filectx(path)
2863
2863
2864 # Return None for removed files.
2864 # Return None for removed files.
2865 if path in wctx.removed():
2865 if path in wctx.removed():
2866 return None
2866 return None
2867
2867
2868 fctx = wctx[path]
2868 fctx = wctx[path]
2869 flags = fctx.flags()
2869 flags = fctx.flags()
2870 mctx = context.memfilectx(
2870 mctx = context.memfilectx(
2871 repo,
2871 repo,
2872 ctx_,
2872 ctx_,
2873 fctx.path(),
2873 fctx.path(),
2874 fctx.data(),
2874 fctx.data(),
2875 islink=b'l' in flags,
2875 islink=b'l' in flags,
2876 isexec=b'x' in flags,
2876 isexec=b'x' in flags,
2877 copysource=copied.get(path),
2877 copysource=copied.get(path),
2878 )
2878 )
2879 return mctx
2879 return mctx
2880 except KeyError:
2880 except KeyError:
2881 return None
2881 return None
2882
2882
2883 else:
2883 else:
2884 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2884 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2885
2885
2886 # Use version of files as in the old cset
2886 # Use version of files as in the old cset
2887 def filectxfn(repo, ctx_, path):
2887 def filectxfn(repo, ctx_, path):
2888 try:
2888 try:
2889 return old.filectx(path)
2889 return old.filectx(path)
2890 except KeyError:
2890 except KeyError:
2891 return None
2891 return None
2892
2892
2893 # See if we got a message from -m or -l, if not, open the editor with
2893 # See if we got a message from -m or -l, if not, open the editor with
2894 # the message of the changeset to amend.
2894 # the message of the changeset to amend.
2895 message = logmessage(ui, opts)
2895 message = logmessage(ui, opts)
2896
2896
2897 editform = mergeeditform(old, b'commit.amend')
2897 editform = mergeeditform(old, b'commit.amend')
2898
2898
2899 if not message:
2899 if not message:
2900 message = old.description()
2900 message = old.description()
2901 # Default if message isn't provided and --edit is not passed is to
2901 # Default if message isn't provided and --edit is not passed is to
2902 # invoke editor, but allow --no-edit. If somehow we don't have any
2902 # invoke editor, but allow --no-edit. If somehow we don't have any
2903 # description, let's always start the editor.
2903 # description, let's always start the editor.
2904 doedit = not message or opts.get(b'edit') in [True, None]
2904 doedit = not message or opts.get(b'edit') in [True, None]
2905 else:
2905 else:
2906 # Default if message is provided is to not invoke editor, but allow
2906 # Default if message is provided is to not invoke editor, but allow
2907 # --edit.
2907 # --edit.
2908 doedit = opts.get(b'edit') is True
2908 doedit = opts.get(b'edit') is True
2909 editor = getcommiteditor(edit=doedit, editform=editform)
2909 editor = getcommiteditor(edit=doedit, editform=editform)
2910
2910
2911 pureextra = extra.copy()
2911 pureextra = extra.copy()
2912 extra[b'amend_source'] = old.hex()
2912 extra[b'amend_source'] = old.hex()
2913
2913
2914 new = context.memctx(
2914 new = context.memctx(
2915 repo,
2915 repo,
2916 parents=[base.node(), old.p2().node()],
2916 parents=[base.node(), old.p2().node()],
2917 text=message,
2917 text=message,
2918 files=files,
2918 files=files,
2919 filectxfn=filectxfn,
2919 filectxfn=filectxfn,
2920 user=user,
2920 user=user,
2921 date=date,
2921 date=date,
2922 extra=extra,
2922 extra=extra,
2923 editor=editor,
2923 editor=editor,
2924 )
2924 )
2925
2925
2926 newdesc = changelog.stripdesc(new.description())
2926 newdesc = changelog.stripdesc(new.description())
2927 if (
2927 if (
2928 (not changes)
2928 (not changes)
2929 and newdesc == old.description()
2929 and newdesc == old.description()
2930 and user == old.user()
2930 and user == old.user()
2931 and (date == old.date() or datemaydiffer)
2931 and (date == old.date() or datemaydiffer)
2932 and pureextra == old.extra()
2932 and pureextra == old.extra()
2933 ):
2933 ):
2934 # nothing changed. continuing here would create a new node
2934 # nothing changed. continuing here would create a new node
2935 # anyway because of the amend_source noise.
2935 # anyway because of the amend_source noise.
2936 #
2936 #
2937 # This not what we expect from amend.
2937 # This not what we expect from amend.
2938 return old.node()
2938 return old.node()
2939
2939
2940 commitphase = None
2940 commitphase = None
2941 if opts.get(b'secret'):
2941 if opts.get(b'secret'):
2942 commitphase = phases.secret
2942 commitphase = phases.secret
2943 newid = repo.commitctx(new)
2943 newid = repo.commitctx(new)
2944 ms.reset()
2944 ms.reset()
2945
2945
2946 # Reroute the working copy parent to the new changeset
2946 # Reroute the working copy parent to the new changeset
2947 repo.setparents(newid, nullid)
2947 repo.setparents(newid, nullid)
2948 mapping = {old.node(): (newid,)}
2948 mapping = {old.node(): (newid,)}
2949 obsmetadata = None
2949 obsmetadata = None
2950 if opts.get(b'note'):
2950 if opts.get(b'note'):
2951 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
2951 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
2952 backup = ui.configbool(b'rewrite', b'backup-bundle')
2952 backup = ui.configbool(b'rewrite', b'backup-bundle')
2953 scmutil.cleanupnodes(
2953 scmutil.cleanupnodes(
2954 repo,
2954 repo,
2955 mapping,
2955 mapping,
2956 b'amend',
2956 b'amend',
2957 metadata=obsmetadata,
2957 metadata=obsmetadata,
2958 fixphase=True,
2958 fixphase=True,
2959 targetphase=commitphase,
2959 targetphase=commitphase,
2960 backup=backup,
2960 backup=backup,
2961 )
2961 )
2962
2962
2963 # Fixing the dirstate because localrepo.commitctx does not update
2963 # Fixing the dirstate because localrepo.commitctx does not update
2964 # it. This is rather convenient because we did not need to update
2964 # it. This is rather convenient because we did not need to update
2965 # the dirstate for all the files in the new commit which commitctx
2965 # the dirstate for all the files in the new commit which commitctx
2966 # could have done if it updated the dirstate. Now, we can
2966 # could have done if it updated the dirstate. Now, we can
2967 # selectively update the dirstate only for the amended files.
2967 # selectively update the dirstate only for the amended files.
2968 dirstate = repo.dirstate
2968 dirstate = repo.dirstate
2969
2969
2970 # Update the state of the files which were added and modified in the
2970 # Update the state of the files which were added and modified in the
2971 # amend to "normal" in the dirstate. We need to use "normallookup" since
2971 # amend to "normal" in the dirstate. We need to use "normallookup" since
2972 # the files may have changed since the command started; using "normal"
2972 # the files may have changed since the command started; using "normal"
2973 # would mark them as clean but with uncommitted contents.
2973 # would mark them as clean but with uncommitted contents.
2974 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2974 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2975 for f in normalfiles:
2975 for f in normalfiles:
2976 dirstate.normallookup(f)
2976 dirstate.normallookup(f)
2977
2977
2978 # Update the state of files which were removed in the amend
2978 # Update the state of files which were removed in the amend
2979 # to "removed" in the dirstate.
2979 # to "removed" in the dirstate.
2980 removedfiles = set(wctx.removed()) & filestoamend
2980 removedfiles = set(wctx.removed()) & filestoamend
2981 for f in removedfiles:
2981 for f in removedfiles:
2982 dirstate.drop(f)
2982 dirstate.drop(f)
2983
2983
2984 return newid
2984 return newid
2985
2985
2986
2986
2987 def commiteditor(repo, ctx, subs, editform=b''):
2987 def commiteditor(repo, ctx, subs, editform=b''):
2988 if ctx.description():
2988 if ctx.description():
2989 return ctx.description()
2989 return ctx.description()
2990 return commitforceeditor(
2990 return commitforceeditor(
2991 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
2991 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
2992 )
2992 )
2993
2993
2994
2994
2995 def commitforceeditor(
2995 def commitforceeditor(
2996 repo,
2996 repo,
2997 ctx,
2997 ctx,
2998 subs,
2998 subs,
2999 finishdesc=None,
2999 finishdesc=None,
3000 extramsg=None,
3000 extramsg=None,
3001 editform=b'',
3001 editform=b'',
3002 unchangedmessagedetection=False,
3002 unchangedmessagedetection=False,
3003 ):
3003 ):
3004 if not extramsg:
3004 if not extramsg:
3005 extramsg = _(b"Leave message empty to abort commit.")
3005 extramsg = _(b"Leave message empty to abort commit.")
3006
3006
3007 forms = [e for e in editform.split(b'.') if e]
3007 forms = [e for e in editform.split(b'.') if e]
3008 forms.insert(0, b'changeset')
3008 forms.insert(0, b'changeset')
3009 templatetext = None
3009 templatetext = None
3010 while forms:
3010 while forms:
3011 ref = b'.'.join(forms)
3011 ref = b'.'.join(forms)
3012 if repo.ui.config(b'committemplate', ref):
3012 if repo.ui.config(b'committemplate', ref):
3013 templatetext = committext = buildcommittemplate(
3013 templatetext = committext = buildcommittemplate(
3014 repo, ctx, subs, extramsg, ref
3014 repo, ctx, subs, extramsg, ref
3015 )
3015 )
3016 break
3016 break
3017 forms.pop()
3017 forms.pop()
3018 else:
3018 else:
3019 committext = buildcommittext(repo, ctx, subs, extramsg)
3019 committext = buildcommittext(repo, ctx, subs, extramsg)
3020
3020
3021 # run editor in the repository root
3021 # run editor in the repository root
3022 olddir = encoding.getcwd()
3022 olddir = encoding.getcwd()
3023 os.chdir(repo.root)
3023 os.chdir(repo.root)
3024
3024
3025 # make in-memory changes visible to external process
3025 # make in-memory changes visible to external process
3026 tr = repo.currenttransaction()
3026 tr = repo.currenttransaction()
3027 repo.dirstate.write(tr)
3027 repo.dirstate.write(tr)
3028 pending = tr and tr.writepending() and repo.root
3028 pending = tr and tr.writepending() and repo.root
3029
3029
3030 editortext = repo.ui.edit(
3030 editortext = repo.ui.edit(
3031 committext,
3031 committext,
3032 ctx.user(),
3032 ctx.user(),
3033 ctx.extra(),
3033 ctx.extra(),
3034 editform=editform,
3034 editform=editform,
3035 pending=pending,
3035 pending=pending,
3036 repopath=repo.path,
3036 repopath=repo.path,
3037 action=b'commit',
3037 action=b'commit',
3038 )
3038 )
3039 text = editortext
3039 text = editortext
3040
3040
3041 # strip away anything below this special string (used for editors that want
3041 # strip away anything below this special string (used for editors that want
3042 # to display the diff)
3042 # to display the diff)
3043 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3043 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3044 if stripbelow:
3044 if stripbelow:
3045 text = text[: stripbelow.start()]
3045 text = text[: stripbelow.start()]
3046
3046
3047 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3047 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3048 os.chdir(olddir)
3048 os.chdir(olddir)
3049
3049
3050 if finishdesc:
3050 if finishdesc:
3051 text = finishdesc(text)
3051 text = finishdesc(text)
3052 if not text.strip():
3052 if not text.strip():
3053 raise error.Abort(_(b"empty commit message"))
3053 raise error.Abort(_(b"empty commit message"))
3054 if unchangedmessagedetection and editortext == templatetext:
3054 if unchangedmessagedetection and editortext == templatetext:
3055 raise error.Abort(_(b"commit message unchanged"))
3055 raise error.Abort(_(b"commit message unchanged"))
3056
3056
3057 return text
3057 return text
3058
3058
3059
3059
3060 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3060 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3061 ui = repo.ui
3061 ui = repo.ui
3062 spec = formatter.reference_templatespec(ref)
3062 spec = formatter.reference_templatespec(ref)
3063 t = logcmdutil.changesettemplater(ui, repo, spec)
3063 t = logcmdutil.changesettemplater(ui, repo, spec)
3064 t.t.cache.update(
3064 t.t.cache.update(
3065 (k, templater.unquotestring(v))
3065 (k, templater.unquotestring(v))
3066 for k, v in repo.ui.configitems(b'committemplate')
3066 for k, v in repo.ui.configitems(b'committemplate')
3067 )
3067 )
3068
3068
3069 if not extramsg:
3069 if not extramsg:
3070 extramsg = b'' # ensure that extramsg is string
3070 extramsg = b'' # ensure that extramsg is string
3071
3071
3072 ui.pushbuffer()
3072 ui.pushbuffer()
3073 t.show(ctx, extramsg=extramsg)
3073 t.show(ctx, extramsg=extramsg)
3074 return ui.popbuffer()
3074 return ui.popbuffer()
3075
3075
3076
3076
3077 def hgprefix(msg):
3077 def hgprefix(msg):
3078 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3078 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3079
3079
3080
3080
3081 def buildcommittext(repo, ctx, subs, extramsg):
3081 def buildcommittext(repo, ctx, subs, extramsg):
3082 edittext = []
3082 edittext = []
3083 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3083 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3084 if ctx.description():
3084 if ctx.description():
3085 edittext.append(ctx.description())
3085 edittext.append(ctx.description())
3086 edittext.append(b"")
3086 edittext.append(b"")
3087 edittext.append(b"") # Empty line between message and comments.
3087 edittext.append(b"") # Empty line between message and comments.
3088 edittext.append(
3088 edittext.append(
3089 hgprefix(
3089 hgprefix(
3090 _(
3090 _(
3091 b"Enter commit message."
3091 b"Enter commit message."
3092 b" Lines beginning with 'HG:' are removed."
3092 b" Lines beginning with 'HG:' are removed."
3093 )
3093 )
3094 )
3094 )
3095 )
3095 )
3096 edittext.append(hgprefix(extramsg))
3096 edittext.append(hgprefix(extramsg))
3097 edittext.append(b"HG: --")
3097 edittext.append(b"HG: --")
3098 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3098 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3099 if ctx.p2():
3099 if ctx.p2():
3100 edittext.append(hgprefix(_(b"branch merge")))
3100 edittext.append(hgprefix(_(b"branch merge")))
3101 if ctx.branch():
3101 if ctx.branch():
3102 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3102 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3103 if bookmarks.isactivewdirparent(repo):
3103 if bookmarks.isactivewdirparent(repo):
3104 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3104 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3105 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3105 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3106 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3106 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3107 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3107 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3108 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3108 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3109 if not added and not modified and not removed:
3109 if not added and not modified and not removed:
3110 edittext.append(hgprefix(_(b"no files changed")))
3110 edittext.append(hgprefix(_(b"no files changed")))
3111 edittext.append(b"")
3111 edittext.append(b"")
3112
3112
3113 return b"\n".join(edittext)
3113 return b"\n".join(edittext)
3114
3114
3115
3115
3116 def commitstatus(repo, node, branch, bheads=None, opts=None):
3116 def commitstatus(repo, node, branch, bheads=None, tip=None, opts=None):
3117 if opts is None:
3117 if opts is None:
3118 opts = {}
3118 opts = {}
3119 ctx = repo[node]
3119 ctx = repo[node]
3120 parents = ctx.parents()
3120 parents = ctx.parents()
3121
3121
3122 if (
3122 if tip is not None and repo.changelog.tip() == tip:
3123 # avoid reporting something like "committed new head" when
3124 # recommitting old changesets, and issue a helpful warning
3125 # for most instances
3126 repo.ui.warn(_("warning: commit already existed in the repository!\n"))
3127 elif (
3123 not opts.get(b'amend')
3128 not opts.get(b'amend')
3124 and bheads
3129 and bheads
3125 and node not in bheads
3130 and node not in bheads
3126 and not any(
3131 and not any(
3127 p.node() in bheads and p.branch() == branch for p in parents
3132 p.node() in bheads and p.branch() == branch for p in parents
3128 )
3133 )
3129 ):
3134 ):
3130 repo.ui.status(_(b'created new head\n'))
3135 repo.ui.status(_(b'created new head\n'))
3131 # The message is not printed for initial roots. For the other
3136 # The message is not printed for initial roots. For the other
3132 # changesets, it is printed in the following situations:
3137 # changesets, it is printed in the following situations:
3133 #
3138 #
3134 # Par column: for the 2 parents with ...
3139 # Par column: for the 2 parents with ...
3135 # N: null or no parent
3140 # N: null or no parent
3136 # B: parent is on another named branch
3141 # B: parent is on another named branch
3137 # C: parent is a regular non head changeset
3142 # C: parent is a regular non head changeset
3138 # H: parent was a branch head of the current branch
3143 # H: parent was a branch head of the current branch
3139 # Msg column: whether we print "created new head" message
3144 # Msg column: whether we print "created new head" message
3140 # In the following, it is assumed that there already exists some
3145 # In the following, it is assumed that there already exists some
3141 # initial branch heads of the current branch, otherwise nothing is
3146 # initial branch heads of the current branch, otherwise nothing is
3142 # printed anyway.
3147 # printed anyway.
3143 #
3148 #
3144 # Par Msg Comment
3149 # Par Msg Comment
3145 # N N y additional topo root
3150 # N N y additional topo root
3146 #
3151 #
3147 # B N y additional branch root
3152 # B N y additional branch root
3148 # C N y additional topo head
3153 # C N y additional topo head
3149 # H N n usual case
3154 # H N n usual case
3150 #
3155 #
3151 # B B y weird additional branch root
3156 # B B y weird additional branch root
3152 # C B y branch merge
3157 # C B y branch merge
3153 # H B n merge with named branch
3158 # H B n merge with named branch
3154 #
3159 #
3155 # C C y additional head from merge
3160 # C C y additional head from merge
3156 # C H n merge with a head
3161 # C H n merge with a head
3157 #
3162 #
3158 # H H n head merge: head count decreases
3163 # H H n head merge: head count decreases
3159
3164
3160 if not opts.get(b'close_branch'):
3165 if not opts.get(b'close_branch'):
3161 for r in parents:
3166 for r in parents:
3162 if r.closesbranch() and r.branch() == branch:
3167 if r.closesbranch() and r.branch() == branch:
3163 repo.ui.status(
3168 repo.ui.status(
3164 _(b'reopening closed branch head %d\n') % r.rev()
3169 _(b'reopening closed branch head %d\n') % r.rev()
3165 )
3170 )
3166
3171
3167 if repo.ui.debugflag:
3172 if repo.ui.debugflag:
3168 repo.ui.write(
3173 repo.ui.write(
3169 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3174 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3170 )
3175 )
3171 elif repo.ui.verbose:
3176 elif repo.ui.verbose:
3172 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3177 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3173
3178
3174
3179
3175 def postcommitstatus(repo, pats, opts):
3180 def postcommitstatus(repo, pats, opts):
3176 return repo.status(match=scmutil.match(repo[None], pats, opts))
3181 return repo.status(match=scmutil.match(repo[None], pats, opts))
3177
3182
3178
3183
3179 def revert(ui, repo, ctx, *pats, **opts):
3184 def revert(ui, repo, ctx, *pats, **opts):
3180 opts = pycompat.byteskwargs(opts)
3185 opts = pycompat.byteskwargs(opts)
3181 parent, p2 = repo.dirstate.parents()
3186 parent, p2 = repo.dirstate.parents()
3182 node = ctx.node()
3187 node = ctx.node()
3183
3188
3184 mf = ctx.manifest()
3189 mf = ctx.manifest()
3185 if node == p2:
3190 if node == p2:
3186 parent = p2
3191 parent = p2
3187
3192
3188 # need all matching names in dirstate and manifest of target rev,
3193 # need all matching names in dirstate and manifest of target rev,
3189 # so have to walk both. do not print errors if files exist in one
3194 # so have to walk both. do not print errors if files exist in one
3190 # but not other. in both cases, filesets should be evaluated against
3195 # but not other. in both cases, filesets should be evaluated against
3191 # workingctx to get consistent result (issue4497). this means 'set:**'
3196 # workingctx to get consistent result (issue4497). this means 'set:**'
3192 # cannot be used to select missing files from target rev.
3197 # cannot be used to select missing files from target rev.
3193
3198
3194 # `names` is a mapping for all elements in working copy and target revision
3199 # `names` is a mapping for all elements in working copy and target revision
3195 # The mapping is in the form:
3200 # The mapping is in the form:
3196 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3201 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3197 names = {}
3202 names = {}
3198 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3203 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3199
3204
3200 with repo.wlock():
3205 with repo.wlock():
3201 ## filling of the `names` mapping
3206 ## filling of the `names` mapping
3202 # walk dirstate to fill `names`
3207 # walk dirstate to fill `names`
3203
3208
3204 interactive = opts.get(b'interactive', False)
3209 interactive = opts.get(b'interactive', False)
3205 wctx = repo[None]
3210 wctx = repo[None]
3206 m = scmutil.match(wctx, pats, opts)
3211 m = scmutil.match(wctx, pats, opts)
3207
3212
3208 # we'll need this later
3213 # we'll need this later
3209 targetsubs = sorted(s for s in wctx.substate if m(s))
3214 targetsubs = sorted(s for s in wctx.substate if m(s))
3210
3215
3211 if not m.always():
3216 if not m.always():
3212 matcher = matchmod.badmatch(m, lambda x, y: False)
3217 matcher = matchmod.badmatch(m, lambda x, y: False)
3213 for abs in wctx.walk(matcher):
3218 for abs in wctx.walk(matcher):
3214 names[abs] = m.exact(abs)
3219 names[abs] = m.exact(abs)
3215
3220
3216 # walk target manifest to fill `names`
3221 # walk target manifest to fill `names`
3217
3222
3218 def badfn(path, msg):
3223 def badfn(path, msg):
3219 if path in names:
3224 if path in names:
3220 return
3225 return
3221 if path in ctx.substate:
3226 if path in ctx.substate:
3222 return
3227 return
3223 path_ = path + b'/'
3228 path_ = path + b'/'
3224 for f in names:
3229 for f in names:
3225 if f.startswith(path_):
3230 if f.startswith(path_):
3226 return
3231 return
3227 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3232 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3228
3233
3229 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3234 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3230 if abs not in names:
3235 if abs not in names:
3231 names[abs] = m.exact(abs)
3236 names[abs] = m.exact(abs)
3232
3237
3233 # Find status of all file in `names`.
3238 # Find status of all file in `names`.
3234 m = scmutil.matchfiles(repo, names)
3239 m = scmutil.matchfiles(repo, names)
3235
3240
3236 changes = repo.status(
3241 changes = repo.status(
3237 node1=node, match=m, unknown=True, ignored=True, clean=True
3242 node1=node, match=m, unknown=True, ignored=True, clean=True
3238 )
3243 )
3239 else:
3244 else:
3240 changes = repo.status(node1=node, match=m)
3245 changes = repo.status(node1=node, match=m)
3241 for kind in changes:
3246 for kind in changes:
3242 for abs in kind:
3247 for abs in kind:
3243 names[abs] = m.exact(abs)
3248 names[abs] = m.exact(abs)
3244
3249
3245 m = scmutil.matchfiles(repo, names)
3250 m = scmutil.matchfiles(repo, names)
3246
3251
3247 modified = set(changes.modified)
3252 modified = set(changes.modified)
3248 added = set(changes.added)
3253 added = set(changes.added)
3249 removed = set(changes.removed)
3254 removed = set(changes.removed)
3250 _deleted = set(changes.deleted)
3255 _deleted = set(changes.deleted)
3251 unknown = set(changes.unknown)
3256 unknown = set(changes.unknown)
3252 unknown.update(changes.ignored)
3257 unknown.update(changes.ignored)
3253 clean = set(changes.clean)
3258 clean = set(changes.clean)
3254 modadded = set()
3259 modadded = set()
3255
3260
3256 # We need to account for the state of the file in the dirstate,
3261 # We need to account for the state of the file in the dirstate,
3257 # even when we revert against something else than parent. This will
3262 # even when we revert against something else than parent. This will
3258 # slightly alter the behavior of revert (doing back up or not, delete
3263 # slightly alter the behavior of revert (doing back up or not, delete
3259 # or just forget etc).
3264 # or just forget etc).
3260 if parent == node:
3265 if parent == node:
3261 dsmodified = modified
3266 dsmodified = modified
3262 dsadded = added
3267 dsadded = added
3263 dsremoved = removed
3268 dsremoved = removed
3264 # store all local modifications, useful later for rename detection
3269 # store all local modifications, useful later for rename detection
3265 localchanges = dsmodified | dsadded
3270 localchanges = dsmodified | dsadded
3266 modified, added, removed = set(), set(), set()
3271 modified, added, removed = set(), set(), set()
3267 else:
3272 else:
3268 changes = repo.status(node1=parent, match=m)
3273 changes = repo.status(node1=parent, match=m)
3269 dsmodified = set(changes.modified)
3274 dsmodified = set(changes.modified)
3270 dsadded = set(changes.added)
3275 dsadded = set(changes.added)
3271 dsremoved = set(changes.removed)
3276 dsremoved = set(changes.removed)
3272 # store all local modifications, useful later for rename detection
3277 # store all local modifications, useful later for rename detection
3273 localchanges = dsmodified | dsadded
3278 localchanges = dsmodified | dsadded
3274
3279
3275 # only take into account for removes between wc and target
3280 # only take into account for removes between wc and target
3276 clean |= dsremoved - removed
3281 clean |= dsremoved - removed
3277 dsremoved &= removed
3282 dsremoved &= removed
3278 # distinct between dirstate remove and other
3283 # distinct between dirstate remove and other
3279 removed -= dsremoved
3284 removed -= dsremoved
3280
3285
3281 modadded = added & dsmodified
3286 modadded = added & dsmodified
3282 added -= modadded
3287 added -= modadded
3283
3288
3284 # tell newly modified apart.
3289 # tell newly modified apart.
3285 dsmodified &= modified
3290 dsmodified &= modified
3286 dsmodified |= modified & dsadded # dirstate added may need backup
3291 dsmodified |= modified & dsadded # dirstate added may need backup
3287 modified -= dsmodified
3292 modified -= dsmodified
3288
3293
3289 # We need to wait for some post-processing to update this set
3294 # We need to wait for some post-processing to update this set
3290 # before making the distinction. The dirstate will be used for
3295 # before making the distinction. The dirstate will be used for
3291 # that purpose.
3296 # that purpose.
3292 dsadded = added
3297 dsadded = added
3293
3298
3294 # in case of merge, files that are actually added can be reported as
3299 # in case of merge, files that are actually added can be reported as
3295 # modified, we need to post process the result
3300 # modified, we need to post process the result
3296 if p2 != nullid:
3301 if p2 != nullid:
3297 mergeadd = set(dsmodified)
3302 mergeadd = set(dsmodified)
3298 for path in dsmodified:
3303 for path in dsmodified:
3299 if path in mf:
3304 if path in mf:
3300 mergeadd.remove(path)
3305 mergeadd.remove(path)
3301 dsadded |= mergeadd
3306 dsadded |= mergeadd
3302 dsmodified -= mergeadd
3307 dsmodified -= mergeadd
3303
3308
3304 # if f is a rename, update `names` to also revert the source
3309 # if f is a rename, update `names` to also revert the source
3305 for f in localchanges:
3310 for f in localchanges:
3306 src = repo.dirstate.copied(f)
3311 src = repo.dirstate.copied(f)
3307 # XXX should we check for rename down to target node?
3312 # XXX should we check for rename down to target node?
3308 if src and src not in names and repo.dirstate[src] == b'r':
3313 if src and src not in names and repo.dirstate[src] == b'r':
3309 dsremoved.add(src)
3314 dsremoved.add(src)
3310 names[src] = True
3315 names[src] = True
3311
3316
3312 # determine the exact nature of the deleted changesets
3317 # determine the exact nature of the deleted changesets
3313 deladded = set(_deleted)
3318 deladded = set(_deleted)
3314 for path in _deleted:
3319 for path in _deleted:
3315 if path in mf:
3320 if path in mf:
3316 deladded.remove(path)
3321 deladded.remove(path)
3317 deleted = _deleted - deladded
3322 deleted = _deleted - deladded
3318
3323
3319 # distinguish between file to forget and the other
3324 # distinguish between file to forget and the other
3320 added = set()
3325 added = set()
3321 for abs in dsadded:
3326 for abs in dsadded:
3322 if repo.dirstate[abs] != b'a':
3327 if repo.dirstate[abs] != b'a':
3323 added.add(abs)
3328 added.add(abs)
3324 dsadded -= added
3329 dsadded -= added
3325
3330
3326 for abs in deladded:
3331 for abs in deladded:
3327 if repo.dirstate[abs] == b'a':
3332 if repo.dirstate[abs] == b'a':
3328 dsadded.add(abs)
3333 dsadded.add(abs)
3329 deladded -= dsadded
3334 deladded -= dsadded
3330
3335
3331 # For files marked as removed, we check if an unknown file is present at
3336 # For files marked as removed, we check if an unknown file is present at
3332 # the same path. If a such file exists it may need to be backed up.
3337 # the same path. If a such file exists it may need to be backed up.
3333 # Making the distinction at this stage helps have simpler backup
3338 # Making the distinction at this stage helps have simpler backup
3334 # logic.
3339 # logic.
3335 removunk = set()
3340 removunk = set()
3336 for abs in removed:
3341 for abs in removed:
3337 target = repo.wjoin(abs)
3342 target = repo.wjoin(abs)
3338 if os.path.lexists(target):
3343 if os.path.lexists(target):
3339 removunk.add(abs)
3344 removunk.add(abs)
3340 removed -= removunk
3345 removed -= removunk
3341
3346
3342 dsremovunk = set()
3347 dsremovunk = set()
3343 for abs in dsremoved:
3348 for abs in dsremoved:
3344 target = repo.wjoin(abs)
3349 target = repo.wjoin(abs)
3345 if os.path.lexists(target):
3350 if os.path.lexists(target):
3346 dsremovunk.add(abs)
3351 dsremovunk.add(abs)
3347 dsremoved -= dsremovunk
3352 dsremoved -= dsremovunk
3348
3353
3349 # action to be actually performed by revert
3354 # action to be actually performed by revert
3350 # (<list of file>, message>) tuple
3355 # (<list of file>, message>) tuple
3351 actions = {
3356 actions = {
3352 b'revert': ([], _(b'reverting %s\n')),
3357 b'revert': ([], _(b'reverting %s\n')),
3353 b'add': ([], _(b'adding %s\n')),
3358 b'add': ([], _(b'adding %s\n')),
3354 b'remove': ([], _(b'removing %s\n')),
3359 b'remove': ([], _(b'removing %s\n')),
3355 b'drop': ([], _(b'removing %s\n')),
3360 b'drop': ([], _(b'removing %s\n')),
3356 b'forget': ([], _(b'forgetting %s\n')),
3361 b'forget': ([], _(b'forgetting %s\n')),
3357 b'undelete': ([], _(b'undeleting %s\n')),
3362 b'undelete': ([], _(b'undeleting %s\n')),
3358 b'noop': (None, _(b'no changes needed to %s\n')),
3363 b'noop': (None, _(b'no changes needed to %s\n')),
3359 b'unknown': (None, _(b'file not managed: %s\n')),
3364 b'unknown': (None, _(b'file not managed: %s\n')),
3360 }
3365 }
3361
3366
3362 # "constant" that convey the backup strategy.
3367 # "constant" that convey the backup strategy.
3363 # All set to `discard` if `no-backup` is set do avoid checking
3368 # All set to `discard` if `no-backup` is set do avoid checking
3364 # no_backup lower in the code.
3369 # no_backup lower in the code.
3365 # These values are ordered for comparison purposes
3370 # These values are ordered for comparison purposes
3366 backupinteractive = 3 # do backup if interactively modified
3371 backupinteractive = 3 # do backup if interactively modified
3367 backup = 2 # unconditionally do backup
3372 backup = 2 # unconditionally do backup
3368 check = 1 # check if the existing file differs from target
3373 check = 1 # check if the existing file differs from target
3369 discard = 0 # never do backup
3374 discard = 0 # never do backup
3370 if opts.get(b'no_backup'):
3375 if opts.get(b'no_backup'):
3371 backupinteractive = backup = check = discard
3376 backupinteractive = backup = check = discard
3372 if interactive:
3377 if interactive:
3373 dsmodifiedbackup = backupinteractive
3378 dsmodifiedbackup = backupinteractive
3374 else:
3379 else:
3375 dsmodifiedbackup = backup
3380 dsmodifiedbackup = backup
3376 tobackup = set()
3381 tobackup = set()
3377
3382
3378 backupanddel = actions[b'remove']
3383 backupanddel = actions[b'remove']
3379 if not opts.get(b'no_backup'):
3384 if not opts.get(b'no_backup'):
3380 backupanddel = actions[b'drop']
3385 backupanddel = actions[b'drop']
3381
3386
3382 disptable = (
3387 disptable = (
3383 # dispatch table:
3388 # dispatch table:
3384 # file state
3389 # file state
3385 # action
3390 # action
3386 # make backup
3391 # make backup
3387 ## Sets that results that will change file on disk
3392 ## Sets that results that will change file on disk
3388 # Modified compared to target, no local change
3393 # Modified compared to target, no local change
3389 (modified, actions[b'revert'], discard),
3394 (modified, actions[b'revert'], discard),
3390 # Modified compared to target, but local file is deleted
3395 # Modified compared to target, but local file is deleted
3391 (deleted, actions[b'revert'], discard),
3396 (deleted, actions[b'revert'], discard),
3392 # Modified compared to target, local change
3397 # Modified compared to target, local change
3393 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3398 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3394 # Added since target
3399 # Added since target
3395 (added, actions[b'remove'], discard),
3400 (added, actions[b'remove'], discard),
3396 # Added in working directory
3401 # Added in working directory
3397 (dsadded, actions[b'forget'], discard),
3402 (dsadded, actions[b'forget'], discard),
3398 # Added since target, have local modification
3403 # Added since target, have local modification
3399 (modadded, backupanddel, backup),
3404 (modadded, backupanddel, backup),
3400 # Added since target but file is missing in working directory
3405 # Added since target but file is missing in working directory
3401 (deladded, actions[b'drop'], discard),
3406 (deladded, actions[b'drop'], discard),
3402 # Removed since target, before working copy parent
3407 # Removed since target, before working copy parent
3403 (removed, actions[b'add'], discard),
3408 (removed, actions[b'add'], discard),
3404 # Same as `removed` but an unknown file exists at the same path
3409 # Same as `removed` but an unknown file exists at the same path
3405 (removunk, actions[b'add'], check),
3410 (removunk, actions[b'add'], check),
3406 # Removed since targe, marked as such in working copy parent
3411 # Removed since targe, marked as such in working copy parent
3407 (dsremoved, actions[b'undelete'], discard),
3412 (dsremoved, actions[b'undelete'], discard),
3408 # Same as `dsremoved` but an unknown file exists at the same path
3413 # Same as `dsremoved` but an unknown file exists at the same path
3409 (dsremovunk, actions[b'undelete'], check),
3414 (dsremovunk, actions[b'undelete'], check),
3410 ## the following sets does not result in any file changes
3415 ## the following sets does not result in any file changes
3411 # File with no modification
3416 # File with no modification
3412 (clean, actions[b'noop'], discard),
3417 (clean, actions[b'noop'], discard),
3413 # Existing file, not tracked anywhere
3418 # Existing file, not tracked anywhere
3414 (unknown, actions[b'unknown'], discard),
3419 (unknown, actions[b'unknown'], discard),
3415 )
3420 )
3416
3421
3417 for abs, exact in sorted(names.items()):
3422 for abs, exact in sorted(names.items()):
3418 # target file to be touch on disk (relative to cwd)
3423 # target file to be touch on disk (relative to cwd)
3419 target = repo.wjoin(abs)
3424 target = repo.wjoin(abs)
3420 # search the entry in the dispatch table.
3425 # search the entry in the dispatch table.
3421 # if the file is in any of these sets, it was touched in the working
3426 # if the file is in any of these sets, it was touched in the working
3422 # directory parent and we are sure it needs to be reverted.
3427 # directory parent and we are sure it needs to be reverted.
3423 for table, (xlist, msg), dobackup in disptable:
3428 for table, (xlist, msg), dobackup in disptable:
3424 if abs not in table:
3429 if abs not in table:
3425 continue
3430 continue
3426 if xlist is not None:
3431 if xlist is not None:
3427 xlist.append(abs)
3432 xlist.append(abs)
3428 if dobackup:
3433 if dobackup:
3429 # If in interactive mode, don't automatically create
3434 # If in interactive mode, don't automatically create
3430 # .orig files (issue4793)
3435 # .orig files (issue4793)
3431 if dobackup == backupinteractive:
3436 if dobackup == backupinteractive:
3432 tobackup.add(abs)
3437 tobackup.add(abs)
3433 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3438 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3434 absbakname = scmutil.backuppath(ui, repo, abs)
3439 absbakname = scmutil.backuppath(ui, repo, abs)
3435 bakname = os.path.relpath(
3440 bakname = os.path.relpath(
3436 absbakname, start=repo.root
3441 absbakname, start=repo.root
3437 )
3442 )
3438 ui.note(
3443 ui.note(
3439 _(b'saving current version of %s as %s\n')
3444 _(b'saving current version of %s as %s\n')
3440 % (uipathfn(abs), uipathfn(bakname))
3445 % (uipathfn(abs), uipathfn(bakname))
3441 )
3446 )
3442 if not opts.get(b'dry_run'):
3447 if not opts.get(b'dry_run'):
3443 if interactive:
3448 if interactive:
3444 util.copyfile(target, absbakname)
3449 util.copyfile(target, absbakname)
3445 else:
3450 else:
3446 util.rename(target, absbakname)
3451 util.rename(target, absbakname)
3447 if opts.get(b'dry_run'):
3452 if opts.get(b'dry_run'):
3448 if ui.verbose or not exact:
3453 if ui.verbose or not exact:
3449 ui.status(msg % uipathfn(abs))
3454 ui.status(msg % uipathfn(abs))
3450 elif exact:
3455 elif exact:
3451 ui.warn(msg % uipathfn(abs))
3456 ui.warn(msg % uipathfn(abs))
3452 break
3457 break
3453
3458
3454 if not opts.get(b'dry_run'):
3459 if not opts.get(b'dry_run'):
3455 needdata = (b'revert', b'add', b'undelete')
3460 needdata = (b'revert', b'add', b'undelete')
3456 oplist = [actions[name][0] for name in needdata]
3461 oplist = [actions[name][0] for name in needdata]
3457 prefetch = scmutil.prefetchfiles
3462 prefetch = scmutil.prefetchfiles
3458 matchfiles = scmutil.matchfiles(
3463 matchfiles = scmutil.matchfiles(
3459 repo, [f for sublist in oplist for f in sublist]
3464 repo, [f for sublist in oplist for f in sublist]
3460 )
3465 )
3461 prefetch(
3466 prefetch(
3462 repo, [(ctx.rev(), matchfiles)],
3467 repo, [(ctx.rev(), matchfiles)],
3463 )
3468 )
3464 match = scmutil.match(repo[None], pats)
3469 match = scmutil.match(repo[None], pats)
3465 _performrevert(
3470 _performrevert(
3466 repo,
3471 repo,
3467 ctx,
3472 ctx,
3468 names,
3473 names,
3469 uipathfn,
3474 uipathfn,
3470 actions,
3475 actions,
3471 match,
3476 match,
3472 interactive,
3477 interactive,
3473 tobackup,
3478 tobackup,
3474 )
3479 )
3475
3480
3476 if targetsubs:
3481 if targetsubs:
3477 # Revert the subrepos on the revert list
3482 # Revert the subrepos on the revert list
3478 for sub in targetsubs:
3483 for sub in targetsubs:
3479 try:
3484 try:
3480 wctx.sub(sub).revert(
3485 wctx.sub(sub).revert(
3481 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3486 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3482 )
3487 )
3483 except KeyError:
3488 except KeyError:
3484 raise error.Abort(
3489 raise error.Abort(
3485 b"subrepository '%s' does not exist in %s!"
3490 b"subrepository '%s' does not exist in %s!"
3486 % (sub, short(ctx.node()))
3491 % (sub, short(ctx.node()))
3487 )
3492 )
3488
3493
3489
3494
3490 def _performrevert(
3495 def _performrevert(
3491 repo,
3496 repo,
3492 ctx,
3497 ctx,
3493 names,
3498 names,
3494 uipathfn,
3499 uipathfn,
3495 actions,
3500 actions,
3496 match,
3501 match,
3497 interactive=False,
3502 interactive=False,
3498 tobackup=None,
3503 tobackup=None,
3499 ):
3504 ):
3500 """function that actually perform all the actions computed for revert
3505 """function that actually perform all the actions computed for revert
3501
3506
3502 This is an independent function to let extension to plug in and react to
3507 This is an independent function to let extension to plug in and react to
3503 the imminent revert.
3508 the imminent revert.
3504
3509
3505 Make sure you have the working directory locked when calling this function.
3510 Make sure you have the working directory locked when calling this function.
3506 """
3511 """
3507 parent, p2 = repo.dirstate.parents()
3512 parent, p2 = repo.dirstate.parents()
3508 node = ctx.node()
3513 node = ctx.node()
3509 excluded_files = []
3514 excluded_files = []
3510
3515
3511 def checkout(f):
3516 def checkout(f):
3512 fc = ctx[f]
3517 fc = ctx[f]
3513 repo.wwrite(f, fc.data(), fc.flags())
3518 repo.wwrite(f, fc.data(), fc.flags())
3514
3519
3515 def doremove(f):
3520 def doremove(f):
3516 try:
3521 try:
3517 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3522 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3518 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3523 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3519 except OSError:
3524 except OSError:
3520 pass
3525 pass
3521 repo.dirstate.remove(f)
3526 repo.dirstate.remove(f)
3522
3527
3523 def prntstatusmsg(action, f):
3528 def prntstatusmsg(action, f):
3524 exact = names[f]
3529 exact = names[f]
3525 if repo.ui.verbose or not exact:
3530 if repo.ui.verbose or not exact:
3526 repo.ui.status(actions[action][1] % uipathfn(f))
3531 repo.ui.status(actions[action][1] % uipathfn(f))
3527
3532
3528 audit_path = pathutil.pathauditor(repo.root, cached=True)
3533 audit_path = pathutil.pathauditor(repo.root, cached=True)
3529 for f in actions[b'forget'][0]:
3534 for f in actions[b'forget'][0]:
3530 if interactive:
3535 if interactive:
3531 choice = repo.ui.promptchoice(
3536 choice = repo.ui.promptchoice(
3532 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3537 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3533 )
3538 )
3534 if choice == 0:
3539 if choice == 0:
3535 prntstatusmsg(b'forget', f)
3540 prntstatusmsg(b'forget', f)
3536 repo.dirstate.drop(f)
3541 repo.dirstate.drop(f)
3537 else:
3542 else:
3538 excluded_files.append(f)
3543 excluded_files.append(f)
3539 else:
3544 else:
3540 prntstatusmsg(b'forget', f)
3545 prntstatusmsg(b'forget', f)
3541 repo.dirstate.drop(f)
3546 repo.dirstate.drop(f)
3542 for f in actions[b'remove'][0]:
3547 for f in actions[b'remove'][0]:
3543 audit_path(f)
3548 audit_path(f)
3544 if interactive:
3549 if interactive:
3545 choice = repo.ui.promptchoice(
3550 choice = repo.ui.promptchoice(
3546 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3551 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3547 )
3552 )
3548 if choice == 0:
3553 if choice == 0:
3549 prntstatusmsg(b'remove', f)
3554 prntstatusmsg(b'remove', f)
3550 doremove(f)
3555 doremove(f)
3551 else:
3556 else:
3552 excluded_files.append(f)
3557 excluded_files.append(f)
3553 else:
3558 else:
3554 prntstatusmsg(b'remove', f)
3559 prntstatusmsg(b'remove', f)
3555 doremove(f)
3560 doremove(f)
3556 for f in actions[b'drop'][0]:
3561 for f in actions[b'drop'][0]:
3557 audit_path(f)
3562 audit_path(f)
3558 prntstatusmsg(b'drop', f)
3563 prntstatusmsg(b'drop', f)
3559 repo.dirstate.remove(f)
3564 repo.dirstate.remove(f)
3560
3565
3561 normal = None
3566 normal = None
3562 if node == parent:
3567 if node == parent:
3563 # We're reverting to our parent. If possible, we'd like status
3568 # We're reverting to our parent. If possible, we'd like status
3564 # to report the file as clean. We have to use normallookup for
3569 # to report the file as clean. We have to use normallookup for
3565 # merges to avoid losing information about merged/dirty files.
3570 # merges to avoid losing information about merged/dirty files.
3566 if p2 != nullid:
3571 if p2 != nullid:
3567 normal = repo.dirstate.normallookup
3572 normal = repo.dirstate.normallookup
3568 else:
3573 else:
3569 normal = repo.dirstate.normal
3574 normal = repo.dirstate.normal
3570
3575
3571 newlyaddedandmodifiedfiles = set()
3576 newlyaddedandmodifiedfiles = set()
3572 if interactive:
3577 if interactive:
3573 # Prompt the user for changes to revert
3578 # Prompt the user for changes to revert
3574 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3579 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3575 m = scmutil.matchfiles(repo, torevert)
3580 m = scmutil.matchfiles(repo, torevert)
3576 diffopts = patch.difffeatureopts(
3581 diffopts = patch.difffeatureopts(
3577 repo.ui,
3582 repo.ui,
3578 whitespace=True,
3583 whitespace=True,
3579 section=b'commands',
3584 section=b'commands',
3580 configprefix=b'revert.interactive.',
3585 configprefix=b'revert.interactive.',
3581 )
3586 )
3582 diffopts.nodates = True
3587 diffopts.nodates = True
3583 diffopts.git = True
3588 diffopts.git = True
3584 operation = b'apply'
3589 operation = b'apply'
3585 if node == parent:
3590 if node == parent:
3586 if repo.ui.configbool(
3591 if repo.ui.configbool(
3587 b'experimental', b'revert.interactive.select-to-keep'
3592 b'experimental', b'revert.interactive.select-to-keep'
3588 ):
3593 ):
3589 operation = b'keep'
3594 operation = b'keep'
3590 else:
3595 else:
3591 operation = b'discard'
3596 operation = b'discard'
3592
3597
3593 if operation == b'apply':
3598 if operation == b'apply':
3594 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3599 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3595 else:
3600 else:
3596 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3601 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3597 originalchunks = patch.parsepatch(diff)
3602 originalchunks = patch.parsepatch(diff)
3598
3603
3599 try:
3604 try:
3600
3605
3601 chunks, opts = recordfilter(
3606 chunks, opts = recordfilter(
3602 repo.ui, originalchunks, match, operation=operation
3607 repo.ui, originalchunks, match, operation=operation
3603 )
3608 )
3604 if operation == b'discard':
3609 if operation == b'discard':
3605 chunks = patch.reversehunks(chunks)
3610 chunks = patch.reversehunks(chunks)
3606
3611
3607 except error.PatchError as err:
3612 except error.PatchError as err:
3608 raise error.Abort(_(b'error parsing patch: %s') % err)
3613 raise error.Abort(_(b'error parsing patch: %s') % err)
3609
3614
3610 # FIXME: when doing an interactive revert of a copy, there's no way of
3615 # FIXME: when doing an interactive revert of a copy, there's no way of
3611 # performing a partial revert of the added file, the only option is
3616 # performing a partial revert of the added file, the only option is
3612 # "remove added file <name> (Yn)?", so we don't need to worry about the
3617 # "remove added file <name> (Yn)?", so we don't need to worry about the
3613 # alsorestore value. Ideally we'd be able to partially revert
3618 # alsorestore value. Ideally we'd be able to partially revert
3614 # copied/renamed files.
3619 # copied/renamed files.
3615 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3620 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3616 chunks, originalchunks
3621 chunks, originalchunks
3617 )
3622 )
3618 if tobackup is None:
3623 if tobackup is None:
3619 tobackup = set()
3624 tobackup = set()
3620 # Apply changes
3625 # Apply changes
3621 fp = stringio()
3626 fp = stringio()
3622 # chunks are serialized per file, but files aren't sorted
3627 # chunks are serialized per file, but files aren't sorted
3623 for f in sorted({c.header.filename() for c in chunks if ishunk(c)}):
3628 for f in sorted({c.header.filename() for c in chunks if ishunk(c)}):
3624 prntstatusmsg(b'revert', f)
3629 prntstatusmsg(b'revert', f)
3625 files = set()
3630 files = set()
3626 for c in chunks:
3631 for c in chunks:
3627 if ishunk(c):
3632 if ishunk(c):
3628 abs = c.header.filename()
3633 abs = c.header.filename()
3629 # Create a backup file only if this hunk should be backed up
3634 # Create a backup file only if this hunk should be backed up
3630 if c.header.filename() in tobackup:
3635 if c.header.filename() in tobackup:
3631 target = repo.wjoin(abs)
3636 target = repo.wjoin(abs)
3632 bakname = scmutil.backuppath(repo.ui, repo, abs)
3637 bakname = scmutil.backuppath(repo.ui, repo, abs)
3633 util.copyfile(target, bakname)
3638 util.copyfile(target, bakname)
3634 tobackup.remove(abs)
3639 tobackup.remove(abs)
3635 if abs not in files:
3640 if abs not in files:
3636 files.add(abs)
3641 files.add(abs)
3637 if operation == b'keep':
3642 if operation == b'keep':
3638 checkout(abs)
3643 checkout(abs)
3639 c.write(fp)
3644 c.write(fp)
3640 dopatch = fp.tell()
3645 dopatch = fp.tell()
3641 fp.seek(0)
3646 fp.seek(0)
3642 if dopatch:
3647 if dopatch:
3643 try:
3648 try:
3644 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3649 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3645 except error.PatchError as err:
3650 except error.PatchError as err:
3646 raise error.Abort(pycompat.bytestr(err))
3651 raise error.Abort(pycompat.bytestr(err))
3647 del fp
3652 del fp
3648 else:
3653 else:
3649 for f in actions[b'revert'][0]:
3654 for f in actions[b'revert'][0]:
3650 prntstatusmsg(b'revert', f)
3655 prntstatusmsg(b'revert', f)
3651 checkout(f)
3656 checkout(f)
3652 if normal:
3657 if normal:
3653 normal(f)
3658 normal(f)
3654
3659
3655 for f in actions[b'add'][0]:
3660 for f in actions[b'add'][0]:
3656 # Don't checkout modified files, they are already created by the diff
3661 # Don't checkout modified files, they are already created by the diff
3657 if f not in newlyaddedandmodifiedfiles:
3662 if f not in newlyaddedandmodifiedfiles:
3658 prntstatusmsg(b'add', f)
3663 prntstatusmsg(b'add', f)
3659 checkout(f)
3664 checkout(f)
3660 repo.dirstate.add(f)
3665 repo.dirstate.add(f)
3661
3666
3662 normal = repo.dirstate.normallookup
3667 normal = repo.dirstate.normallookup
3663 if node == parent and p2 == nullid:
3668 if node == parent and p2 == nullid:
3664 normal = repo.dirstate.normal
3669 normal = repo.dirstate.normal
3665 for f in actions[b'undelete'][0]:
3670 for f in actions[b'undelete'][0]:
3666 if interactive:
3671 if interactive:
3667 choice = repo.ui.promptchoice(
3672 choice = repo.ui.promptchoice(
3668 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3673 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3669 )
3674 )
3670 if choice == 0:
3675 if choice == 0:
3671 prntstatusmsg(b'undelete', f)
3676 prntstatusmsg(b'undelete', f)
3672 checkout(f)
3677 checkout(f)
3673 normal(f)
3678 normal(f)
3674 else:
3679 else:
3675 excluded_files.append(f)
3680 excluded_files.append(f)
3676 else:
3681 else:
3677 prntstatusmsg(b'undelete', f)
3682 prntstatusmsg(b'undelete', f)
3678 checkout(f)
3683 checkout(f)
3679 normal(f)
3684 normal(f)
3680
3685
3681 copied = copies.pathcopies(repo[parent], ctx)
3686 copied = copies.pathcopies(repo[parent], ctx)
3682
3687
3683 for f in (
3688 for f in (
3684 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3689 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3685 ):
3690 ):
3686 if f in copied:
3691 if f in copied:
3687 repo.dirstate.copy(copied[f], f)
3692 repo.dirstate.copy(copied[f], f)
3688
3693
3689
3694
3690 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3695 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3691 # commands.outgoing. "missing" is "missing" of the result of
3696 # commands.outgoing. "missing" is "missing" of the result of
3692 # "findcommonoutgoing()"
3697 # "findcommonoutgoing()"
3693 outgoinghooks = util.hooks()
3698 outgoinghooks = util.hooks()
3694
3699
3695 # a list of (ui, repo) functions called by commands.summary
3700 # a list of (ui, repo) functions called by commands.summary
3696 summaryhooks = util.hooks()
3701 summaryhooks = util.hooks()
3697
3702
3698 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3703 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3699 #
3704 #
3700 # functions should return tuple of booleans below, if 'changes' is None:
3705 # functions should return tuple of booleans below, if 'changes' is None:
3701 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3706 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3702 #
3707 #
3703 # otherwise, 'changes' is a tuple of tuples below:
3708 # otherwise, 'changes' is a tuple of tuples below:
3704 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3709 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3705 # - (desturl, destbranch, destpeer, outgoing)
3710 # - (desturl, destbranch, destpeer, outgoing)
3706 summaryremotehooks = util.hooks()
3711 summaryremotehooks = util.hooks()
3707
3712
3708
3713
3709 def checkunfinished(repo, commit=False, skipmerge=False):
3714 def checkunfinished(repo, commit=False, skipmerge=False):
3710 '''Look for an unfinished multistep operation, like graft, and abort
3715 '''Look for an unfinished multistep operation, like graft, and abort
3711 if found. It's probably good to check this right before
3716 if found. It's probably good to check this right before
3712 bailifchanged().
3717 bailifchanged().
3713 '''
3718 '''
3714 # Check for non-clearable states first, so things like rebase will take
3719 # Check for non-clearable states first, so things like rebase will take
3715 # precedence over update.
3720 # precedence over update.
3716 for state in statemod._unfinishedstates:
3721 for state in statemod._unfinishedstates:
3717 if (
3722 if (
3718 state._clearable
3723 state._clearable
3719 or (commit and state._allowcommit)
3724 or (commit and state._allowcommit)
3720 or state._reportonly
3725 or state._reportonly
3721 ):
3726 ):
3722 continue
3727 continue
3723 if state.isunfinished(repo):
3728 if state.isunfinished(repo):
3724 raise error.Abort(state.msg(), hint=state.hint())
3729 raise error.Abort(state.msg(), hint=state.hint())
3725
3730
3726 for s in statemod._unfinishedstates:
3731 for s in statemod._unfinishedstates:
3727 if (
3732 if (
3728 not s._clearable
3733 not s._clearable
3729 or (commit and s._allowcommit)
3734 or (commit and s._allowcommit)
3730 or (s._opname == b'merge' and skipmerge)
3735 or (s._opname == b'merge' and skipmerge)
3731 or s._reportonly
3736 or s._reportonly
3732 ):
3737 ):
3733 continue
3738 continue
3734 if s.isunfinished(repo):
3739 if s.isunfinished(repo):
3735 raise error.Abort(s.msg(), hint=s.hint())
3740 raise error.Abort(s.msg(), hint=s.hint())
3736
3741
3737
3742
3738 def clearunfinished(repo):
3743 def clearunfinished(repo):
3739 '''Check for unfinished operations (as above), and clear the ones
3744 '''Check for unfinished operations (as above), and clear the ones
3740 that are clearable.
3745 that are clearable.
3741 '''
3746 '''
3742 for state in statemod._unfinishedstates:
3747 for state in statemod._unfinishedstates:
3743 if state._reportonly:
3748 if state._reportonly:
3744 continue
3749 continue
3745 if not state._clearable and state.isunfinished(repo):
3750 if not state._clearable and state.isunfinished(repo):
3746 raise error.Abort(state.msg(), hint=state.hint())
3751 raise error.Abort(state.msg(), hint=state.hint())
3747
3752
3748 for s in statemod._unfinishedstates:
3753 for s in statemod._unfinishedstates:
3749 if s._opname == b'merge' or state._reportonly:
3754 if s._opname == b'merge' or state._reportonly:
3750 continue
3755 continue
3751 if s._clearable and s.isunfinished(repo):
3756 if s._clearable and s.isunfinished(repo):
3752 util.unlink(repo.vfs.join(s._fname))
3757 util.unlink(repo.vfs.join(s._fname))
3753
3758
3754
3759
3755 def getunfinishedstate(repo):
3760 def getunfinishedstate(repo):
3756 ''' Checks for unfinished operations and returns statecheck object
3761 ''' Checks for unfinished operations and returns statecheck object
3757 for it'''
3762 for it'''
3758 for state in statemod._unfinishedstates:
3763 for state in statemod._unfinishedstates:
3759 if state.isunfinished(repo):
3764 if state.isunfinished(repo):
3760 return state
3765 return state
3761 return None
3766 return None
3762
3767
3763
3768
3764 def howtocontinue(repo):
3769 def howtocontinue(repo):
3765 '''Check for an unfinished operation and return the command to finish
3770 '''Check for an unfinished operation and return the command to finish
3766 it.
3771 it.
3767
3772
3768 statemod._unfinishedstates list is checked for an unfinished operation
3773 statemod._unfinishedstates list is checked for an unfinished operation
3769 and the corresponding message to finish it is generated if a method to
3774 and the corresponding message to finish it is generated if a method to
3770 continue is supported by the operation.
3775 continue is supported by the operation.
3771
3776
3772 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3777 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3773 a boolean.
3778 a boolean.
3774 '''
3779 '''
3775 contmsg = _(b"continue: %s")
3780 contmsg = _(b"continue: %s")
3776 for state in statemod._unfinishedstates:
3781 for state in statemod._unfinishedstates:
3777 if not state._continueflag:
3782 if not state._continueflag:
3778 continue
3783 continue
3779 if state.isunfinished(repo):
3784 if state.isunfinished(repo):
3780 return contmsg % state.continuemsg(), True
3785 return contmsg % state.continuemsg(), True
3781 if repo[None].dirty(missing=True, merge=False, branch=False):
3786 if repo[None].dirty(missing=True, merge=False, branch=False):
3782 return contmsg % _(b"hg commit"), False
3787 return contmsg % _(b"hg commit"), False
3783 return None, None
3788 return None, None
3784
3789
3785
3790
3786 def checkafterresolved(repo):
3791 def checkafterresolved(repo):
3787 '''Inform the user about the next action after completing hg resolve
3792 '''Inform the user about the next action after completing hg resolve
3788
3793
3789 If there's a an unfinished operation that supports continue flag,
3794 If there's a an unfinished operation that supports continue flag,
3790 howtocontinue will yield repo.ui.warn as the reporter.
3795 howtocontinue will yield repo.ui.warn as the reporter.
3791
3796
3792 Otherwise, it will yield repo.ui.note.
3797 Otherwise, it will yield repo.ui.note.
3793 '''
3798 '''
3794 msg, warning = howtocontinue(repo)
3799 msg, warning = howtocontinue(repo)
3795 if msg is not None:
3800 if msg is not None:
3796 if warning:
3801 if warning:
3797 repo.ui.warn(b"%s\n" % msg)
3802 repo.ui.warn(b"%s\n" % msg)
3798 else:
3803 else:
3799 repo.ui.note(b"%s\n" % msg)
3804 repo.ui.note(b"%s\n" % msg)
3800
3805
3801
3806
3802 def wrongtooltocontinue(repo, task):
3807 def wrongtooltocontinue(repo, task):
3803 '''Raise an abort suggesting how to properly continue if there is an
3808 '''Raise an abort suggesting how to properly continue if there is an
3804 active task.
3809 active task.
3805
3810
3806 Uses howtocontinue() to find the active task.
3811 Uses howtocontinue() to find the active task.
3807
3812
3808 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3813 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3809 a hint.
3814 a hint.
3810 '''
3815 '''
3811 after = howtocontinue(repo)
3816 after = howtocontinue(repo)
3812 hint = None
3817 hint = None
3813 if after[1]:
3818 if after[1]:
3814 hint = after[0]
3819 hint = after[0]
3815 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3820 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3816
3821
3817
3822
3818 def abortgraft(ui, repo, graftstate):
3823 def abortgraft(ui, repo, graftstate):
3819 """abort the interrupted graft and rollbacks to the state before interrupted
3824 """abort the interrupted graft and rollbacks to the state before interrupted
3820 graft"""
3825 graft"""
3821 if not graftstate.exists():
3826 if not graftstate.exists():
3822 raise error.Abort(_(b"no interrupted graft to abort"))
3827 raise error.Abort(_(b"no interrupted graft to abort"))
3823 statedata = readgraftstate(repo, graftstate)
3828 statedata = readgraftstate(repo, graftstate)
3824 newnodes = statedata.get(b'newnodes')
3829 newnodes = statedata.get(b'newnodes')
3825 if newnodes is None:
3830 if newnodes is None:
3826 # and old graft state which does not have all the data required to abort
3831 # and old graft state which does not have all the data required to abort
3827 # the graft
3832 # the graft
3828 raise error.Abort(_(b"cannot abort using an old graftstate"))
3833 raise error.Abort(_(b"cannot abort using an old graftstate"))
3829
3834
3830 # changeset from which graft operation was started
3835 # changeset from which graft operation was started
3831 if len(newnodes) > 0:
3836 if len(newnodes) > 0:
3832 startctx = repo[newnodes[0]].p1()
3837 startctx = repo[newnodes[0]].p1()
3833 else:
3838 else:
3834 startctx = repo[b'.']
3839 startctx = repo[b'.']
3835 # whether to strip or not
3840 # whether to strip or not
3836 cleanup = False
3841 cleanup = False
3837
3842
3838 if newnodes:
3843 if newnodes:
3839 newnodes = [repo[r].rev() for r in newnodes]
3844 newnodes = [repo[r].rev() for r in newnodes]
3840 cleanup = True
3845 cleanup = True
3841 # checking that none of the newnodes turned public or is public
3846 # checking that none of the newnodes turned public or is public
3842 immutable = [c for c in newnodes if not repo[c].mutable()]
3847 immutable = [c for c in newnodes if not repo[c].mutable()]
3843 if immutable:
3848 if immutable:
3844 repo.ui.warn(
3849 repo.ui.warn(
3845 _(b"cannot clean up public changesets %s\n")
3850 _(b"cannot clean up public changesets %s\n")
3846 % b', '.join(bytes(repo[r]) for r in immutable),
3851 % b', '.join(bytes(repo[r]) for r in immutable),
3847 hint=_(b"see 'hg help phases' for details"),
3852 hint=_(b"see 'hg help phases' for details"),
3848 )
3853 )
3849 cleanup = False
3854 cleanup = False
3850
3855
3851 # checking that no new nodes are created on top of grafted revs
3856 # checking that no new nodes are created on top of grafted revs
3852 desc = set(repo.changelog.descendants(newnodes))
3857 desc = set(repo.changelog.descendants(newnodes))
3853 if desc - set(newnodes):
3858 if desc - set(newnodes):
3854 repo.ui.warn(
3859 repo.ui.warn(
3855 _(
3860 _(
3856 b"new changesets detected on destination "
3861 b"new changesets detected on destination "
3857 b"branch, can't strip\n"
3862 b"branch, can't strip\n"
3858 )
3863 )
3859 )
3864 )
3860 cleanup = False
3865 cleanup = False
3861
3866
3862 if cleanup:
3867 if cleanup:
3863 with repo.wlock(), repo.lock():
3868 with repo.wlock(), repo.lock():
3864 mergemod.clean_update(startctx)
3869 mergemod.clean_update(startctx)
3865 # stripping the new nodes created
3870 # stripping the new nodes created
3866 strippoints = [
3871 strippoints = [
3867 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3872 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3868 ]
3873 ]
3869 repair.strip(repo.ui, repo, strippoints, backup=False)
3874 repair.strip(repo.ui, repo, strippoints, backup=False)
3870
3875
3871 if not cleanup:
3876 if not cleanup:
3872 # we don't update to the startnode if we can't strip
3877 # we don't update to the startnode if we can't strip
3873 startctx = repo[b'.']
3878 startctx = repo[b'.']
3874 mergemod.clean_update(startctx)
3879 mergemod.clean_update(startctx)
3875
3880
3876 ui.status(_(b"graft aborted\n"))
3881 ui.status(_(b"graft aborted\n"))
3877 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3882 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3878 graftstate.delete()
3883 graftstate.delete()
3879 return 0
3884 return 0
3880
3885
3881
3886
3882 def readgraftstate(repo, graftstate):
3887 def readgraftstate(repo, graftstate):
3883 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3888 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3884 """read the graft state file and return a dict of the data stored in it"""
3889 """read the graft state file and return a dict of the data stored in it"""
3885 try:
3890 try:
3886 return graftstate.read()
3891 return graftstate.read()
3887 except error.CorruptedState:
3892 except error.CorruptedState:
3888 nodes = repo.vfs.read(b'graftstate').splitlines()
3893 nodes = repo.vfs.read(b'graftstate').splitlines()
3889 return {b'nodes': nodes}
3894 return {b'nodes': nodes}
3890
3895
3891
3896
3892 def hgabortgraft(ui, repo):
3897 def hgabortgraft(ui, repo):
3893 """ abort logic for aborting graft using 'hg abort'"""
3898 """ abort logic for aborting graft using 'hg abort'"""
3894 with repo.wlock():
3899 with repo.wlock():
3895 graftstate = statemod.cmdstate(repo, b'graftstate')
3900 graftstate = statemod.cmdstate(repo, b'graftstate')
3896 return abortgraft(ui, repo, graftstate)
3901 return abortgraft(ui, repo, graftstate)
@@ -1,7673 +1,7677 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 errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 wdirhex,
21 wdirhex,
22 wdirrev,
22 wdirrev,
23 )
23 )
24 from .pycompat import open
24 from .pycompat import open
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 bundlecaches,
29 bundlecaches,
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 grep as grepmod,
44 grep as grepmod,
45 hbisect,
45 hbisect,
46 help,
46 help,
47 hg,
47 hg,
48 logcmdutil,
48 logcmdutil,
49 merge as mergemod,
49 merge as mergemod,
50 mergestate as mergestatemod,
50 mergestate as mergestatemod,
51 narrowspec,
51 narrowspec,
52 obsolete,
52 obsolete,
53 obsutil,
53 obsutil,
54 patch,
54 patch,
55 phases,
55 phases,
56 pycompat,
56 pycompat,
57 rcutil,
57 rcutil,
58 registrar,
58 registrar,
59 requirements,
59 requirements,
60 revsetlang,
60 revsetlang,
61 rewriteutil,
61 rewriteutil,
62 scmutil,
62 scmutil,
63 server,
63 server,
64 shelve as shelvemod,
64 shelve as shelvemod,
65 state as statemod,
65 state as statemod,
66 streamclone,
66 streamclone,
67 tags as tagsmod,
67 tags as tagsmod,
68 ui as uimod,
68 ui as uimod,
69 util,
69 util,
70 verify as verifymod,
70 verify as verifymod,
71 vfs as vfsmod,
71 vfs as vfsmod,
72 wireprotoserver,
72 wireprotoserver,
73 )
73 )
74 from .utils import (
74 from .utils import (
75 dateutil,
75 dateutil,
76 stringutil,
76 stringutil,
77 )
77 )
78
78
79 table = {}
79 table = {}
80 table.update(debugcommandsmod.command._table)
80 table.update(debugcommandsmod.command._table)
81
81
82 command = registrar.command(table)
82 command = registrar.command(table)
83 INTENT_READONLY = registrar.INTENT_READONLY
83 INTENT_READONLY = registrar.INTENT_READONLY
84
84
85 # common command options
85 # common command options
86
86
87 globalopts = [
87 globalopts = [
88 (
88 (
89 b'R',
89 b'R',
90 b'repository',
90 b'repository',
91 b'',
91 b'',
92 _(b'repository root directory or name of overlay bundle file'),
92 _(b'repository root directory or name of overlay bundle file'),
93 _(b'REPO'),
93 _(b'REPO'),
94 ),
94 ),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
96 (
96 (
97 b'y',
97 b'y',
98 b'noninteractive',
98 b'noninteractive',
99 None,
99 None,
100 _(
100 _(
101 b'do not prompt, automatically pick the first choice for all prompts'
101 b'do not prompt, automatically pick the first choice for all prompts'
102 ),
102 ),
103 ),
103 ),
104 (b'q', b'quiet', None, _(b'suppress output')),
104 (b'q', b'quiet', None, _(b'suppress output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
106 (
106 (
107 b'',
107 b'',
108 b'color',
108 b'color',
109 b'',
109 b'',
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
111 # and should not be translated
111 # and should not be translated
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
113 _(b'TYPE'),
113 _(b'TYPE'),
114 ),
114 ),
115 (
115 (
116 b'',
116 b'',
117 b'config',
117 b'config',
118 [],
118 [],
119 _(b'set/override config option (use \'section.name=value\')'),
119 _(b'set/override config option (use \'section.name=value\')'),
120 _(b'CONFIG'),
120 _(b'CONFIG'),
121 ),
121 ),
122 (b'', b'debug', None, _(b'enable debugging output')),
122 (b'', b'debug', None, _(b'enable debugging output')),
123 (b'', b'debugger', None, _(b'start debugger')),
123 (b'', b'debugger', None, _(b'start debugger')),
124 (
124 (
125 b'',
125 b'',
126 b'encoding',
126 b'encoding',
127 encoding.encoding,
127 encoding.encoding,
128 _(b'set the charset encoding'),
128 _(b'set the charset encoding'),
129 _(b'ENCODE'),
129 _(b'ENCODE'),
130 ),
130 ),
131 (
131 (
132 b'',
132 b'',
133 b'encodingmode',
133 b'encodingmode',
134 encoding.encodingmode,
134 encoding.encodingmode,
135 _(b'set the charset encoding mode'),
135 _(b'set the charset encoding mode'),
136 _(b'MODE'),
136 _(b'MODE'),
137 ),
137 ),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
139 (b'', b'time', None, _(b'time how long the command takes')),
139 (b'', b'time', None, _(b'time how long the command takes')),
140 (b'', b'profile', None, _(b'print command execution profile')),
140 (b'', b'profile', None, _(b'print command execution profile')),
141 (b'', b'version', None, _(b'output version information and exit')),
141 (b'', b'version', None, _(b'output version information and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
144 (
144 (
145 b'',
145 b'',
146 b'pager',
146 b'pager',
147 b'auto',
147 b'auto',
148 _(b"when to paginate (boolean, always, auto, or never)"),
148 _(b"when to paginate (boolean, always, auto, or never)"),
149 _(b'TYPE'),
149 _(b'TYPE'),
150 ),
150 ),
151 ]
151 ]
152
152
153 dryrunopts = cmdutil.dryrunopts
153 dryrunopts = cmdutil.dryrunopts
154 remoteopts = cmdutil.remoteopts
154 remoteopts = cmdutil.remoteopts
155 walkopts = cmdutil.walkopts
155 walkopts = cmdutil.walkopts
156 commitopts = cmdutil.commitopts
156 commitopts = cmdutil.commitopts
157 commitopts2 = cmdutil.commitopts2
157 commitopts2 = cmdutil.commitopts2
158 commitopts3 = cmdutil.commitopts3
158 commitopts3 = cmdutil.commitopts3
159 formatteropts = cmdutil.formatteropts
159 formatteropts = cmdutil.formatteropts
160 templateopts = cmdutil.templateopts
160 templateopts = cmdutil.templateopts
161 logopts = cmdutil.logopts
161 logopts = cmdutil.logopts
162 diffopts = cmdutil.diffopts
162 diffopts = cmdutil.diffopts
163 diffwsopts = cmdutil.diffwsopts
163 diffwsopts = cmdutil.diffwsopts
164 diffopts2 = cmdutil.diffopts2
164 diffopts2 = cmdutil.diffopts2
165 mergetoolopts = cmdutil.mergetoolopts
165 mergetoolopts = cmdutil.mergetoolopts
166 similarityopts = cmdutil.similarityopts
166 similarityopts = cmdutil.similarityopts
167 subrepoopts = cmdutil.subrepoopts
167 subrepoopts = cmdutil.subrepoopts
168 debugrevlogopts = cmdutil.debugrevlogopts
168 debugrevlogopts = cmdutil.debugrevlogopts
169
169
170 # Commands start here, listed alphabetically
170 # Commands start here, listed alphabetically
171
171
172
172
173 @command(
173 @command(
174 b'abort',
174 b'abort',
175 dryrunopts,
175 dryrunopts,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
177 helpbasic=True,
177 helpbasic=True,
178 )
178 )
179 def abort(ui, repo, **opts):
179 def abort(ui, repo, **opts):
180 """abort an unfinished operation (EXPERIMENTAL)
180 """abort an unfinished operation (EXPERIMENTAL)
181
181
182 Aborts a multistep operation like graft, histedit, rebase, merge,
182 Aborts a multistep operation like graft, histedit, rebase, merge,
183 and unshelve if they are in an unfinished state.
183 and unshelve if they are in an unfinished state.
184
184
185 use --dry-run/-n to dry run the command.
185 use --dry-run/-n to dry run the command.
186 """
186 """
187 dryrun = opts.get('dry_run')
187 dryrun = opts.get('dry_run')
188 abortstate = cmdutil.getunfinishedstate(repo)
188 abortstate = cmdutil.getunfinishedstate(repo)
189 if not abortstate:
189 if not abortstate:
190 raise error.Abort(_(b'no operation in progress'))
190 raise error.Abort(_(b'no operation in progress'))
191 if not abortstate.abortfunc:
191 if not abortstate.abortfunc:
192 raise error.Abort(
192 raise error.Abort(
193 (
193 (
194 _(b"%s in progress but does not support 'hg abort'")
194 _(b"%s in progress but does not support 'hg abort'")
195 % (abortstate._opname)
195 % (abortstate._opname)
196 ),
196 ),
197 hint=abortstate.hint(),
197 hint=abortstate.hint(),
198 )
198 )
199 if dryrun:
199 if dryrun:
200 ui.status(
200 ui.status(
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
202 )
202 )
203 return
203 return
204 return abortstate.abortfunc(ui, repo)
204 return abortstate.abortfunc(ui, repo)
205
205
206
206
207 @command(
207 @command(
208 b'add',
208 b'add',
209 walkopts + subrepoopts + dryrunopts,
209 walkopts + subrepoopts + dryrunopts,
210 _(b'[OPTION]... [FILE]...'),
210 _(b'[OPTION]... [FILE]...'),
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
212 helpbasic=True,
212 helpbasic=True,
213 inferrepo=True,
213 inferrepo=True,
214 )
214 )
215 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
216 """add the specified files on the next commit
216 """add the specified files on the next commit
217
217
218 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
219 repository.
219 repository.
220
220
221 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
222 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
223
223
224 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
225 files matching ``.hgignore``).
225 files matching ``.hgignore``).
226
226
227 .. container:: verbose
227 .. container:: verbose
228
228
229 Examples:
229 Examples:
230
230
231 - New (unknown) files are added
231 - New (unknown) files are added
232 automatically by :hg:`add`::
232 automatically by :hg:`add`::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ? foo.c
237 ? foo.c
238 $ hg add
238 $ hg add
239 adding foo.c
239 adding foo.c
240 $ hg status
240 $ hg status
241 A foo.c
241 A foo.c
242
242
243 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
244
244
245 $ ls
245 $ ls
246 bar.c foo.c
246 bar.c foo.c
247 $ hg status
247 $ hg status
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg add bar.c
250 $ hg add bar.c
251 $ hg status
251 $ hg status
252 A bar.c
252 A bar.c
253 ? foo.c
253 ? foo.c
254
254
255 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
256 """
256 """
257
257
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
261 return rejected and 1 or 0
261 return rejected and 1 or 0
262
262
263
263
264 @command(
264 @command(
265 b'addremove',
265 b'addremove',
266 similarityopts + subrepoopts + walkopts + dryrunopts,
266 similarityopts + subrepoopts + walkopts + dryrunopts,
267 _(b'[OPTION]... [FILE]...'),
267 _(b'[OPTION]... [FILE]...'),
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
269 inferrepo=True,
269 inferrepo=True,
270 )
270 )
271 def addremove(ui, repo, *pats, **opts):
271 def addremove(ui, repo, *pats, **opts):
272 """add all new files, delete all missing files
272 """add all new files, delete all missing files
273
273
274 Add all new files and remove all missing files from the
274 Add all new files and remove all missing files from the
275 repository.
275 repository.
276
276
277 Unless names are given, new files are ignored if they match any of
277 Unless names are given, new files are ignored if they match any of
278 the patterns in ``.hgignore``. As with add, these changes take
278 the patterns in ``.hgignore``. As with add, these changes take
279 effect at the next commit.
279 effect at the next commit.
280
280
281 Use the -s/--similarity option to detect renamed files. This
281 Use the -s/--similarity option to detect renamed files. This
282 option takes a percentage between 0 (disabled) and 100 (files must
282 option takes a percentage between 0 (disabled) and 100 (files must
283 be identical) as its parameter. With a parameter greater than 0,
283 be identical) as its parameter. With a parameter greater than 0,
284 this compares every removed file with every added file and records
284 this compares every removed file with every added file and records
285 those similar enough as renames. Detecting renamed files this way
285 those similar enough as renames. Detecting renamed files this way
286 can be expensive. After using this option, :hg:`status -C` can be
286 can be expensive. After using this option, :hg:`status -C` can be
287 used to check which files were identified as moved or renamed. If
287 used to check which files were identified as moved or renamed. If
288 not specified, -s/--similarity defaults to 100 and only renames of
288 not specified, -s/--similarity defaults to 100 and only renames of
289 identical files are detected.
289 identical files are detected.
290
290
291 .. container:: verbose
291 .. container:: verbose
292
292
293 Examples:
293 Examples:
294
294
295 - A number of files (bar.c and foo.c) are new,
295 - A number of files (bar.c and foo.c) are new,
296 while foobar.c has been removed (without using :hg:`remove`)
296 while foobar.c has been removed (without using :hg:`remove`)
297 from the repository::
297 from the repository::
298
298
299 $ ls
299 $ ls
300 bar.c foo.c
300 bar.c foo.c
301 $ hg status
301 $ hg status
302 ! foobar.c
302 ! foobar.c
303 ? bar.c
303 ? bar.c
304 ? foo.c
304 ? foo.c
305 $ hg addremove
305 $ hg addremove
306 adding bar.c
306 adding bar.c
307 adding foo.c
307 adding foo.c
308 removing foobar.c
308 removing foobar.c
309 $ hg status
309 $ hg status
310 A bar.c
310 A bar.c
311 A foo.c
311 A foo.c
312 R foobar.c
312 R foobar.c
313
313
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
315 Afterwards, it was edited slightly::
315 Afterwards, it was edited slightly::
316
316
317 $ ls
317 $ ls
318 foo.c
318 foo.c
319 $ hg status
319 $ hg status
320 ! foobar.c
320 ! foobar.c
321 ? foo.c
321 ? foo.c
322 $ hg addremove --similarity 90
322 $ hg addremove --similarity 90
323 removing foobar.c
323 removing foobar.c
324 adding foo.c
324 adding foo.c
325 recording removal of foobar.c as rename to foo.c (94% similar)
325 recording removal of foobar.c as rename to foo.c (94% similar)
326 $ hg status -C
326 $ hg status -C
327 A foo.c
327 A foo.c
328 foobar.c
328 foobar.c
329 R foobar.c
329 R foobar.c
330
330
331 Returns 0 if all files are successfully added.
331 Returns 0 if all files are successfully added.
332 """
332 """
333 opts = pycompat.byteskwargs(opts)
333 opts = pycompat.byteskwargs(opts)
334 if not opts.get(b'similarity'):
334 if not opts.get(b'similarity'):
335 opts[b'similarity'] = b'100'
335 opts[b'similarity'] = b'100'
336 matcher = scmutil.match(repo[None], pats, opts)
336 matcher = scmutil.match(repo[None], pats, opts)
337 relative = scmutil.anypats(pats, opts)
337 relative = scmutil.anypats(pats, opts)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
340
340
341
341
342 @command(
342 @command(
343 b'annotate|blame',
343 b'annotate|blame',
344 [
344 [
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
346 (
346 (
347 b'',
347 b'',
348 b'follow',
348 b'follow',
349 None,
349 None,
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
351 ),
351 ),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
353 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'a', b'text', None, _(b'treat all files as text')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
355 (b'f', b'file', None, _(b'list the filename')),
355 (b'f', b'file', None, _(b'list the filename')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
359 (
359 (
360 b'l',
360 b'l',
361 b'line-number',
361 b'line-number',
362 None,
362 None,
363 _(b'show line number at the first appearance'),
363 _(b'show line number at the first appearance'),
364 ),
364 ),
365 (
365 (
366 b'',
366 b'',
367 b'skip',
367 b'skip',
368 [],
368 [],
369 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'revset to not display (EXPERIMENTAL)'),
370 _(b'REV'),
370 _(b'REV'),
371 ),
371 ),
372 ]
372 ]
373 + diffwsopts
373 + diffwsopts
374 + walkopts
374 + walkopts
375 + formatteropts,
375 + formatteropts,
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
378 helpbasic=True,
378 helpbasic=True,
379 inferrepo=True,
379 inferrepo=True,
380 )
380 )
381 def annotate(ui, repo, *pats, **opts):
381 def annotate(ui, repo, *pats, **opts):
382 """show changeset information by line for each file
382 """show changeset information by line for each file
383
383
384 List changes in files, showing the revision id responsible for
384 List changes in files, showing the revision id responsible for
385 each line.
385 each line.
386
386
387 This command is useful for discovering when a change was made and
387 This command is useful for discovering when a change was made and
388 by whom.
388 by whom.
389
389
390 If you include --file, --user, or --date, the revision number is
390 If you include --file, --user, or --date, the revision number is
391 suppressed unless you also include --number.
391 suppressed unless you also include --number.
392
392
393 Without the -a/--text option, annotate will avoid processing files
393 Without the -a/--text option, annotate will avoid processing files
394 it detects as binary. With -a, annotate will annotate the file
394 it detects as binary. With -a, annotate will annotate the file
395 anyway, although the results will probably be neither useful
395 anyway, although the results will probably be neither useful
396 nor desirable.
396 nor desirable.
397
397
398 .. container:: verbose
398 .. container:: verbose
399
399
400 Template:
400 Template:
401
401
402 The following keywords are supported in addition to the common template
402 The following keywords are supported in addition to the common template
403 keywords and functions. See also :hg:`help templates`.
403 keywords and functions. See also :hg:`help templates`.
404
404
405 :lines: List of lines with annotation data.
405 :lines: List of lines with annotation data.
406 :path: String. Repository-absolute path of the specified file.
406 :path: String. Repository-absolute path of the specified file.
407
407
408 And each entry of ``{lines}`` provides the following sub-keywords in
408 And each entry of ``{lines}`` provides the following sub-keywords in
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
410
410
411 :line: String. Line content.
411 :line: String. Line content.
412 :lineno: Integer. Line number at that revision.
412 :lineno: Integer. Line number at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
414
414
415 See :hg:`help templates.operators` for the list expansion syntax.
415 See :hg:`help templates.operators` for the list expansion syntax.
416
416
417 Returns 0 on success.
417 Returns 0 on success.
418 """
418 """
419 opts = pycompat.byteskwargs(opts)
419 opts = pycompat.byteskwargs(opts)
420 if not pats:
420 if not pats:
421 raise error.Abort(_(b'at least one filename or pattern is required'))
421 raise error.Abort(_(b'at least one filename or pattern is required'))
422
422
423 if opts.get(b'follow'):
423 if opts.get(b'follow'):
424 # --follow is deprecated and now just an alias for -f/--file
424 # --follow is deprecated and now just an alias for -f/--file
425 # to mimic the behavior of Mercurial before version 1.5
425 # to mimic the behavior of Mercurial before version 1.5
426 opts[b'file'] = True
426 opts[b'file'] = True
427
427
428 if (
428 if (
429 not opts.get(b'user')
429 not opts.get(b'user')
430 and not opts.get(b'changeset')
430 and not opts.get(b'changeset')
431 and not opts.get(b'date')
431 and not opts.get(b'date')
432 and not opts.get(b'file')
432 and not opts.get(b'file')
433 ):
433 ):
434 opts[b'number'] = True
434 opts[b'number'] = True
435
435
436 linenumber = opts.get(b'line_number') is not None
436 linenumber = opts.get(b'line_number') is not None
437 if (
437 if (
438 linenumber
438 linenumber
439 and (not opts.get(b'changeset'))
439 and (not opts.get(b'changeset'))
440 and (not opts.get(b'number'))
440 and (not opts.get(b'number'))
441 ):
441 ):
442 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
442 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
443
443
444 rev = opts.get(b'rev')
444 rev = opts.get(b'rev')
445 if rev:
445 if rev:
446 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
446 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
447 ctx = scmutil.revsingle(repo, rev)
447 ctx = scmutil.revsingle(repo, rev)
448
448
449 ui.pager(b'annotate')
449 ui.pager(b'annotate')
450 rootfm = ui.formatter(b'annotate', opts)
450 rootfm = ui.formatter(b'annotate', opts)
451 if ui.debugflag:
451 if ui.debugflag:
452 shorthex = pycompat.identity
452 shorthex = pycompat.identity
453 else:
453 else:
454
454
455 def shorthex(h):
455 def shorthex(h):
456 return h[:12]
456 return h[:12]
457
457
458 if ui.quiet:
458 if ui.quiet:
459 datefunc = dateutil.shortdate
459 datefunc = dateutil.shortdate
460 else:
460 else:
461 datefunc = dateutil.datestr
461 datefunc = dateutil.datestr
462 if ctx.rev() is None:
462 if ctx.rev() is None:
463 if opts.get(b'changeset'):
463 if opts.get(b'changeset'):
464 # omit "+" suffix which is appended to node hex
464 # omit "+" suffix which is appended to node hex
465 def formatrev(rev):
465 def formatrev(rev):
466 if rev == wdirrev:
466 if rev == wdirrev:
467 return b'%d' % ctx.p1().rev()
467 return b'%d' % ctx.p1().rev()
468 else:
468 else:
469 return b'%d' % rev
469 return b'%d' % rev
470
470
471 else:
471 else:
472
472
473 def formatrev(rev):
473 def formatrev(rev):
474 if rev == wdirrev:
474 if rev == wdirrev:
475 return b'%d+' % ctx.p1().rev()
475 return b'%d+' % ctx.p1().rev()
476 else:
476 else:
477 return b'%d ' % rev
477 return b'%d ' % rev
478
478
479 def formathex(h):
479 def formathex(h):
480 if h == wdirhex:
480 if h == wdirhex:
481 return b'%s+' % shorthex(hex(ctx.p1().node()))
481 return b'%s+' % shorthex(hex(ctx.p1().node()))
482 else:
482 else:
483 return b'%s ' % shorthex(h)
483 return b'%s ' % shorthex(h)
484
484
485 else:
485 else:
486 formatrev = b'%d'.__mod__
486 formatrev = b'%d'.__mod__
487 formathex = shorthex
487 formathex = shorthex
488
488
489 opmap = [
489 opmap = [
490 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
490 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
491 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
491 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
492 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
492 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
493 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
493 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
494 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
494 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
495 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
495 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
496 ]
496 ]
497 opnamemap = {
497 opnamemap = {
498 b'rev': b'number',
498 b'rev': b'number',
499 b'node': b'changeset',
499 b'node': b'changeset',
500 b'path': b'file',
500 b'path': b'file',
501 b'lineno': b'line_number',
501 b'lineno': b'line_number',
502 }
502 }
503
503
504 if rootfm.isplain():
504 if rootfm.isplain():
505
505
506 def makefunc(get, fmt):
506 def makefunc(get, fmt):
507 return lambda x: fmt(get(x))
507 return lambda x: fmt(get(x))
508
508
509 else:
509 else:
510
510
511 def makefunc(get, fmt):
511 def makefunc(get, fmt):
512 return get
512 return get
513
513
514 datahint = rootfm.datahint()
514 datahint = rootfm.datahint()
515 funcmap = [
515 funcmap = [
516 (makefunc(get, fmt), sep)
516 (makefunc(get, fmt), sep)
517 for fn, sep, get, fmt in opmap
517 for fn, sep, get, fmt in opmap
518 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
518 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
519 ]
519 ]
520 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
520 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
521 fields = b' '.join(
521 fields = b' '.join(
522 fn
522 fn
523 for fn, sep, get, fmt in opmap
523 for fn, sep, get, fmt in opmap
524 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
524 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
525 )
525 )
526
526
527 def bad(x, y):
527 def bad(x, y):
528 raise error.Abort(b"%s: %s" % (x, y))
528 raise error.Abort(b"%s: %s" % (x, y))
529
529
530 m = scmutil.match(ctx, pats, opts, badfn=bad)
530 m = scmutil.match(ctx, pats, opts, badfn=bad)
531
531
532 follow = not opts.get(b'no_follow')
532 follow = not opts.get(b'no_follow')
533 diffopts = patch.difffeatureopts(
533 diffopts = patch.difffeatureopts(
534 ui, opts, section=b'annotate', whitespace=True
534 ui, opts, section=b'annotate', whitespace=True
535 )
535 )
536 skiprevs = opts.get(b'skip')
536 skiprevs = opts.get(b'skip')
537 if skiprevs:
537 if skiprevs:
538 skiprevs = scmutil.revrange(repo, skiprevs)
538 skiprevs = scmutil.revrange(repo, skiprevs)
539
539
540 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
540 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
541 for abs in ctx.walk(m):
541 for abs in ctx.walk(m):
542 fctx = ctx[abs]
542 fctx = ctx[abs]
543 rootfm.startitem()
543 rootfm.startitem()
544 rootfm.data(path=abs)
544 rootfm.data(path=abs)
545 if not opts.get(b'text') and fctx.isbinary():
545 if not opts.get(b'text') and fctx.isbinary():
546 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
546 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
547 continue
547 continue
548
548
549 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
549 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
550 lines = fctx.annotate(
550 lines = fctx.annotate(
551 follow=follow, skiprevs=skiprevs, diffopts=diffopts
551 follow=follow, skiprevs=skiprevs, diffopts=diffopts
552 )
552 )
553 if not lines:
553 if not lines:
554 fm.end()
554 fm.end()
555 continue
555 continue
556 formats = []
556 formats = []
557 pieces = []
557 pieces = []
558
558
559 for f, sep in funcmap:
559 for f, sep in funcmap:
560 l = [f(n) for n in lines]
560 l = [f(n) for n in lines]
561 if fm.isplain():
561 if fm.isplain():
562 sizes = [encoding.colwidth(x) for x in l]
562 sizes = [encoding.colwidth(x) for x in l]
563 ml = max(sizes)
563 ml = max(sizes)
564 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
564 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
565 else:
565 else:
566 formats.append([b'%s'] * len(l))
566 formats.append([b'%s'] * len(l))
567 pieces.append(l)
567 pieces.append(l)
568
568
569 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
569 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
570 fm.startitem()
570 fm.startitem()
571 fm.context(fctx=n.fctx)
571 fm.context(fctx=n.fctx)
572 fm.write(fields, b"".join(f), *p)
572 fm.write(fields, b"".join(f), *p)
573 if n.skip:
573 if n.skip:
574 fmt = b"* %s"
574 fmt = b"* %s"
575 else:
575 else:
576 fmt = b": %s"
576 fmt = b": %s"
577 fm.write(b'line', fmt, n.text)
577 fm.write(b'line', fmt, n.text)
578
578
579 if not lines[-1].text.endswith(b'\n'):
579 if not lines[-1].text.endswith(b'\n'):
580 fm.plain(b'\n')
580 fm.plain(b'\n')
581 fm.end()
581 fm.end()
582
582
583 rootfm.end()
583 rootfm.end()
584
584
585
585
586 @command(
586 @command(
587 b'archive',
587 b'archive',
588 [
588 [
589 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
589 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
590 (
590 (
591 b'p',
591 b'p',
592 b'prefix',
592 b'prefix',
593 b'',
593 b'',
594 _(b'directory prefix for files in archive'),
594 _(b'directory prefix for files in archive'),
595 _(b'PREFIX'),
595 _(b'PREFIX'),
596 ),
596 ),
597 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
597 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
598 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
598 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
599 ]
599 ]
600 + subrepoopts
600 + subrepoopts
601 + walkopts,
601 + walkopts,
602 _(b'[OPTION]... DEST'),
602 _(b'[OPTION]... DEST'),
603 helpcategory=command.CATEGORY_IMPORT_EXPORT,
603 helpcategory=command.CATEGORY_IMPORT_EXPORT,
604 )
604 )
605 def archive(ui, repo, dest, **opts):
605 def archive(ui, repo, dest, **opts):
606 '''create an unversioned archive of a repository revision
606 '''create an unversioned archive of a repository revision
607
607
608 By default, the revision used is the parent of the working
608 By default, the revision used is the parent of the working
609 directory; use -r/--rev to specify a different revision.
609 directory; use -r/--rev to specify a different revision.
610
610
611 The archive type is automatically detected based on file
611 The archive type is automatically detected based on file
612 extension (to override, use -t/--type).
612 extension (to override, use -t/--type).
613
613
614 .. container:: verbose
614 .. container:: verbose
615
615
616 Examples:
616 Examples:
617
617
618 - create a zip file containing the 1.0 release::
618 - create a zip file containing the 1.0 release::
619
619
620 hg archive -r 1.0 project-1.0.zip
620 hg archive -r 1.0 project-1.0.zip
621
621
622 - create a tarball excluding .hg files::
622 - create a tarball excluding .hg files::
623
623
624 hg archive project.tar.gz -X ".hg*"
624 hg archive project.tar.gz -X ".hg*"
625
625
626 Valid types are:
626 Valid types are:
627
627
628 :``files``: a directory full of files (default)
628 :``files``: a directory full of files (default)
629 :``tar``: tar archive, uncompressed
629 :``tar``: tar archive, uncompressed
630 :``tbz2``: tar archive, compressed using bzip2
630 :``tbz2``: tar archive, compressed using bzip2
631 :``tgz``: tar archive, compressed using gzip
631 :``tgz``: tar archive, compressed using gzip
632 :``txz``: tar archive, compressed using lzma (only in Python 3)
632 :``txz``: tar archive, compressed using lzma (only in Python 3)
633 :``uzip``: zip archive, uncompressed
633 :``uzip``: zip archive, uncompressed
634 :``zip``: zip archive, compressed using deflate
634 :``zip``: zip archive, compressed using deflate
635
635
636 The exact name of the destination archive or directory is given
636 The exact name of the destination archive or directory is given
637 using a format string; see :hg:`help export` for details.
637 using a format string; see :hg:`help export` for details.
638
638
639 Each member added to an archive file has a directory prefix
639 Each member added to an archive file has a directory prefix
640 prepended. Use -p/--prefix to specify a format string for the
640 prepended. Use -p/--prefix to specify a format string for the
641 prefix. The default is the basename of the archive, with suffixes
641 prefix. The default is the basename of the archive, with suffixes
642 removed.
642 removed.
643
643
644 Returns 0 on success.
644 Returns 0 on success.
645 '''
645 '''
646
646
647 opts = pycompat.byteskwargs(opts)
647 opts = pycompat.byteskwargs(opts)
648 rev = opts.get(b'rev')
648 rev = opts.get(b'rev')
649 if rev:
649 if rev:
650 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
650 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
651 ctx = scmutil.revsingle(repo, rev)
651 ctx = scmutil.revsingle(repo, rev)
652 if not ctx:
652 if not ctx:
653 raise error.Abort(_(b'no working directory: please specify a revision'))
653 raise error.Abort(_(b'no working directory: please specify a revision'))
654 node = ctx.node()
654 node = ctx.node()
655 dest = cmdutil.makefilename(ctx, dest)
655 dest = cmdutil.makefilename(ctx, dest)
656 if os.path.realpath(dest) == repo.root:
656 if os.path.realpath(dest) == repo.root:
657 raise error.Abort(_(b'repository root cannot be destination'))
657 raise error.Abort(_(b'repository root cannot be destination'))
658
658
659 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
659 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
660 prefix = opts.get(b'prefix')
660 prefix = opts.get(b'prefix')
661
661
662 if dest == b'-':
662 if dest == b'-':
663 if kind == b'files':
663 if kind == b'files':
664 raise error.Abort(_(b'cannot archive plain files to stdout'))
664 raise error.Abort(_(b'cannot archive plain files to stdout'))
665 dest = cmdutil.makefileobj(ctx, dest)
665 dest = cmdutil.makefileobj(ctx, dest)
666 if not prefix:
666 if not prefix:
667 prefix = os.path.basename(repo.root) + b'-%h'
667 prefix = os.path.basename(repo.root) + b'-%h'
668
668
669 prefix = cmdutil.makefilename(ctx, prefix)
669 prefix = cmdutil.makefilename(ctx, prefix)
670 match = scmutil.match(ctx, [], opts)
670 match = scmutil.match(ctx, [], opts)
671 archival.archive(
671 archival.archive(
672 repo,
672 repo,
673 dest,
673 dest,
674 node,
674 node,
675 kind,
675 kind,
676 not opts.get(b'no_decode'),
676 not opts.get(b'no_decode'),
677 match,
677 match,
678 prefix,
678 prefix,
679 subrepos=opts.get(b'subrepos'),
679 subrepos=opts.get(b'subrepos'),
680 )
680 )
681
681
682
682
683 @command(
683 @command(
684 b'backout',
684 b'backout',
685 [
685 [
686 (
686 (
687 b'',
687 b'',
688 b'merge',
688 b'merge',
689 None,
689 None,
690 _(b'merge with old dirstate parent after backout'),
690 _(b'merge with old dirstate parent after backout'),
691 ),
691 ),
692 (
692 (
693 b'',
693 b'',
694 b'commit',
694 b'commit',
695 None,
695 None,
696 _(b'commit if no conflicts were encountered (DEPRECATED)'),
696 _(b'commit if no conflicts were encountered (DEPRECATED)'),
697 ),
697 ),
698 (b'', b'no-commit', None, _(b'do not commit')),
698 (b'', b'no-commit', None, _(b'do not commit')),
699 (
699 (
700 b'',
700 b'',
701 b'parent',
701 b'parent',
702 b'',
702 b'',
703 _(b'parent to choose when backing out merge (DEPRECATED)'),
703 _(b'parent to choose when backing out merge (DEPRECATED)'),
704 _(b'REV'),
704 _(b'REV'),
705 ),
705 ),
706 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
706 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
707 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
707 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
708 ]
708 ]
709 + mergetoolopts
709 + mergetoolopts
710 + walkopts
710 + walkopts
711 + commitopts
711 + commitopts
712 + commitopts2,
712 + commitopts2,
713 _(b'[OPTION]... [-r] REV'),
713 _(b'[OPTION]... [-r] REV'),
714 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
714 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
715 )
715 )
716 def backout(ui, repo, node=None, rev=None, **opts):
716 def backout(ui, repo, node=None, rev=None, **opts):
717 '''reverse effect of earlier changeset
717 '''reverse effect of earlier changeset
718
718
719 Prepare a new changeset with the effect of REV undone in the
719 Prepare a new changeset with the effect of REV undone in the
720 current working directory. If no conflicts were encountered,
720 current working directory. If no conflicts were encountered,
721 it will be committed immediately.
721 it will be committed immediately.
722
722
723 If REV is the parent of the working directory, then this new changeset
723 If REV is the parent of the working directory, then this new changeset
724 is committed automatically (unless --no-commit is specified).
724 is committed automatically (unless --no-commit is specified).
725
725
726 .. note::
726 .. note::
727
727
728 :hg:`backout` cannot be used to fix either an unwanted or
728 :hg:`backout` cannot be used to fix either an unwanted or
729 incorrect merge.
729 incorrect merge.
730
730
731 .. container:: verbose
731 .. container:: verbose
732
732
733 Examples:
733 Examples:
734
734
735 - Reverse the effect of the parent of the working directory.
735 - Reverse the effect of the parent of the working directory.
736 This backout will be committed immediately::
736 This backout will be committed immediately::
737
737
738 hg backout -r .
738 hg backout -r .
739
739
740 - Reverse the effect of previous bad revision 23::
740 - Reverse the effect of previous bad revision 23::
741
741
742 hg backout -r 23
742 hg backout -r 23
743
743
744 - Reverse the effect of previous bad revision 23 and
744 - Reverse the effect of previous bad revision 23 and
745 leave changes uncommitted::
745 leave changes uncommitted::
746
746
747 hg backout -r 23 --no-commit
747 hg backout -r 23 --no-commit
748 hg commit -m "Backout revision 23"
748 hg commit -m "Backout revision 23"
749
749
750 By default, the pending changeset will have one parent,
750 By default, the pending changeset will have one parent,
751 maintaining a linear history. With --merge, the pending
751 maintaining a linear history. With --merge, the pending
752 changeset will instead have two parents: the old parent of the
752 changeset will instead have two parents: the old parent of the
753 working directory and a new child of REV that simply undoes REV.
753 working directory and a new child of REV that simply undoes REV.
754
754
755 Before version 1.7, the behavior without --merge was equivalent
755 Before version 1.7, the behavior without --merge was equivalent
756 to specifying --merge followed by :hg:`update --clean .` to
756 to specifying --merge followed by :hg:`update --clean .` to
757 cancel the merge and leave the child of REV as a head to be
757 cancel the merge and leave the child of REV as a head to be
758 merged separately.
758 merged separately.
759
759
760 See :hg:`help dates` for a list of formats valid for -d/--date.
760 See :hg:`help dates` for a list of formats valid for -d/--date.
761
761
762 See :hg:`help revert` for a way to restore files to the state
762 See :hg:`help revert` for a way to restore files to the state
763 of another revision.
763 of another revision.
764
764
765 Returns 0 on success, 1 if nothing to backout or there are unresolved
765 Returns 0 on success, 1 if nothing to backout or there are unresolved
766 files.
766 files.
767 '''
767 '''
768 with repo.wlock(), repo.lock():
768 with repo.wlock(), repo.lock():
769 return _dobackout(ui, repo, node, rev, **opts)
769 return _dobackout(ui, repo, node, rev, **opts)
770
770
771
771
772 def _dobackout(ui, repo, node=None, rev=None, **opts):
772 def _dobackout(ui, repo, node=None, rev=None, **opts):
773 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
773 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
774 opts = pycompat.byteskwargs(opts)
774 opts = pycompat.byteskwargs(opts)
775
775
776 if rev and node:
776 if rev and node:
777 raise error.Abort(_(b"please specify just one revision"))
777 raise error.Abort(_(b"please specify just one revision"))
778
778
779 if not rev:
779 if not rev:
780 rev = node
780 rev = node
781
781
782 if not rev:
782 if not rev:
783 raise error.Abort(_(b"please specify a revision to backout"))
783 raise error.Abort(_(b"please specify a revision to backout"))
784
784
785 date = opts.get(b'date')
785 date = opts.get(b'date')
786 if date:
786 if date:
787 opts[b'date'] = dateutil.parsedate(date)
787 opts[b'date'] = dateutil.parsedate(date)
788
788
789 cmdutil.checkunfinished(repo)
789 cmdutil.checkunfinished(repo)
790 cmdutil.bailifchanged(repo)
790 cmdutil.bailifchanged(repo)
791 ctx = scmutil.revsingle(repo, rev)
791 ctx = scmutil.revsingle(repo, rev)
792 node = ctx.node()
792 node = ctx.node()
793
793
794 op1, op2 = repo.dirstate.parents()
794 op1, op2 = repo.dirstate.parents()
795 if not repo.changelog.isancestor(node, op1):
795 if not repo.changelog.isancestor(node, op1):
796 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
796 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
797
797
798 p1, p2 = repo.changelog.parents(node)
798 p1, p2 = repo.changelog.parents(node)
799 if p1 == nullid:
799 if p1 == nullid:
800 raise error.Abort(_(b'cannot backout a change with no parents'))
800 raise error.Abort(_(b'cannot backout a change with no parents'))
801 if p2 != nullid:
801 if p2 != nullid:
802 if not opts.get(b'parent'):
802 if not opts.get(b'parent'):
803 raise error.Abort(_(b'cannot backout a merge changeset'))
803 raise error.Abort(_(b'cannot backout a merge changeset'))
804 p = repo.lookup(opts[b'parent'])
804 p = repo.lookup(opts[b'parent'])
805 if p not in (p1, p2):
805 if p not in (p1, p2):
806 raise error.Abort(
806 raise error.Abort(
807 _(b'%s is not a parent of %s') % (short(p), short(node))
807 _(b'%s is not a parent of %s') % (short(p), short(node))
808 )
808 )
809 parent = p
809 parent = p
810 else:
810 else:
811 if opts.get(b'parent'):
811 if opts.get(b'parent'):
812 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
812 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
813 parent = p1
813 parent = p1
814
814
815 # the backout should appear on the same branch
815 # the backout should appear on the same branch
816 branch = repo.dirstate.branch()
816 branch = repo.dirstate.branch()
817 bheads = repo.branchheads(branch)
817 bheads = repo.branchheads(branch)
818 rctx = scmutil.revsingle(repo, hex(parent))
818 rctx = scmutil.revsingle(repo, hex(parent))
819 if not opts.get(b'merge') and op1 != node:
819 if not opts.get(b'merge') and op1 != node:
820 with dirstateguard.dirstateguard(repo, b'backout'):
820 with dirstateguard.dirstateguard(repo, b'backout'):
821 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
821 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
822 with ui.configoverride(overrides, b'backout'):
822 with ui.configoverride(overrides, b'backout'):
823 stats = mergemod.back_out(ctx, parent=repo[parent])
823 stats = mergemod.back_out(ctx, parent=repo[parent])
824 repo.setparents(op1, op2)
824 repo.setparents(op1, op2)
825 hg._showstats(repo, stats)
825 hg._showstats(repo, stats)
826 if stats.unresolvedcount:
826 if stats.unresolvedcount:
827 repo.ui.status(
827 repo.ui.status(
828 _(b"use 'hg resolve' to retry unresolved file merges\n")
828 _(b"use 'hg resolve' to retry unresolved file merges\n")
829 )
829 )
830 return 1
830 return 1
831 else:
831 else:
832 hg.clean(repo, node, show_stats=False)
832 hg.clean(repo, node, show_stats=False)
833 repo.dirstate.setbranch(branch)
833 repo.dirstate.setbranch(branch)
834 cmdutil.revert(ui, repo, rctx)
834 cmdutil.revert(ui, repo, rctx)
835
835
836 if opts.get(b'no_commit'):
836 if opts.get(b'no_commit'):
837 msg = _(b"changeset %s backed out, don't forget to commit.\n")
837 msg = _(b"changeset %s backed out, don't forget to commit.\n")
838 ui.status(msg % short(node))
838 ui.status(msg % short(node))
839 return 0
839 return 0
840
840
841 def commitfunc(ui, repo, message, match, opts):
841 def commitfunc(ui, repo, message, match, opts):
842 editform = b'backout'
842 editform = b'backout'
843 e = cmdutil.getcommiteditor(
843 e = cmdutil.getcommiteditor(
844 editform=editform, **pycompat.strkwargs(opts)
844 editform=editform, **pycompat.strkwargs(opts)
845 )
845 )
846 if not message:
846 if not message:
847 # we don't translate commit messages
847 # we don't translate commit messages
848 message = b"Backed out changeset %s" % short(node)
848 message = b"Backed out changeset %s" % short(node)
849 e = cmdutil.getcommiteditor(edit=True, editform=editform)
849 e = cmdutil.getcommiteditor(edit=True, editform=editform)
850 return repo.commit(
850 return repo.commit(
851 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
851 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
852 )
852 )
853
853
854 # save to detect changes
855 tip = repo.changelog.tip()
856
854 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
857 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
855 if not newnode:
858 if not newnode:
856 ui.status(_(b"nothing changed\n"))
859 ui.status(_(b"nothing changed\n"))
857 return 1
860 return 1
858 cmdutil.commitstatus(repo, newnode, branch, bheads)
861 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
859
862
860 def nice(node):
863 def nice(node):
861 return b'%d:%s' % (repo.changelog.rev(node), short(node))
864 return b'%d:%s' % (repo.changelog.rev(node), short(node))
862
865
863 ui.status(
866 ui.status(
864 _(b'changeset %s backs out changeset %s\n')
867 _(b'changeset %s backs out changeset %s\n')
865 % (nice(newnode), nice(node))
868 % (nice(newnode), nice(node))
866 )
869 )
867 if opts.get(b'merge') and op1 != node:
870 if opts.get(b'merge') and op1 != node:
868 hg.clean(repo, op1, show_stats=False)
871 hg.clean(repo, op1, show_stats=False)
869 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
872 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
870 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
873 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
871 with ui.configoverride(overrides, b'backout'):
874 with ui.configoverride(overrides, b'backout'):
872 return hg.merge(repo[b'tip'])
875 return hg.merge(repo[b'tip'])
873 return 0
876 return 0
874
877
875
878
876 @command(
879 @command(
877 b'bisect',
880 b'bisect',
878 [
881 [
879 (b'r', b'reset', False, _(b'reset bisect state')),
882 (b'r', b'reset', False, _(b'reset bisect state')),
880 (b'g', b'good', False, _(b'mark changeset good')),
883 (b'g', b'good', False, _(b'mark changeset good')),
881 (b'b', b'bad', False, _(b'mark changeset bad')),
884 (b'b', b'bad', False, _(b'mark changeset bad')),
882 (b's', b'skip', False, _(b'skip testing changeset')),
885 (b's', b'skip', False, _(b'skip testing changeset')),
883 (b'e', b'extend', False, _(b'extend the bisect range')),
886 (b'e', b'extend', False, _(b'extend the bisect range')),
884 (
887 (
885 b'c',
888 b'c',
886 b'command',
889 b'command',
887 b'',
890 b'',
888 _(b'use command to check changeset state'),
891 _(b'use command to check changeset state'),
889 _(b'CMD'),
892 _(b'CMD'),
890 ),
893 ),
891 (b'U', b'noupdate', False, _(b'do not update to target')),
894 (b'U', b'noupdate', False, _(b'do not update to target')),
892 ],
895 ],
893 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
896 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
894 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
897 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
895 )
898 )
896 def bisect(
899 def bisect(
897 ui,
900 ui,
898 repo,
901 repo,
899 rev=None,
902 rev=None,
900 extra=None,
903 extra=None,
901 command=None,
904 command=None,
902 reset=None,
905 reset=None,
903 good=None,
906 good=None,
904 bad=None,
907 bad=None,
905 skip=None,
908 skip=None,
906 extend=None,
909 extend=None,
907 noupdate=None,
910 noupdate=None,
908 ):
911 ):
909 """subdivision search of changesets
912 """subdivision search of changesets
910
913
911 This command helps to find changesets which introduce problems. To
914 This command helps to find changesets which introduce problems. To
912 use, mark the earliest changeset you know exhibits the problem as
915 use, mark the earliest changeset you know exhibits the problem as
913 bad, then mark the latest changeset which is free from the problem
916 bad, then mark the latest changeset which is free from the problem
914 as good. Bisect will update your working directory to a revision
917 as good. Bisect will update your working directory to a revision
915 for testing (unless the -U/--noupdate option is specified). Once
918 for testing (unless the -U/--noupdate option is specified). Once
916 you have performed tests, mark the working directory as good or
919 you have performed tests, mark the working directory as good or
917 bad, and bisect will either update to another candidate changeset
920 bad, and bisect will either update to another candidate changeset
918 or announce that it has found the bad revision.
921 or announce that it has found the bad revision.
919
922
920 As a shortcut, you can also use the revision argument to mark a
923 As a shortcut, you can also use the revision argument to mark a
921 revision as good or bad without checking it out first.
924 revision as good or bad without checking it out first.
922
925
923 If you supply a command, it will be used for automatic bisection.
926 If you supply a command, it will be used for automatic bisection.
924 The environment variable HG_NODE will contain the ID of the
927 The environment variable HG_NODE will contain the ID of the
925 changeset being tested. The exit status of the command will be
928 changeset being tested. The exit status of the command will be
926 used to mark revisions as good or bad: status 0 means good, 125
929 used to mark revisions as good or bad: status 0 means good, 125
927 means to skip the revision, 127 (command not found) will abort the
930 means to skip the revision, 127 (command not found) will abort the
928 bisection, and any other non-zero exit status means the revision
931 bisection, and any other non-zero exit status means the revision
929 is bad.
932 is bad.
930
933
931 .. container:: verbose
934 .. container:: verbose
932
935
933 Some examples:
936 Some examples:
934
937
935 - start a bisection with known bad revision 34, and good revision 12::
938 - start a bisection with known bad revision 34, and good revision 12::
936
939
937 hg bisect --bad 34
940 hg bisect --bad 34
938 hg bisect --good 12
941 hg bisect --good 12
939
942
940 - advance the current bisection by marking current revision as good or
943 - advance the current bisection by marking current revision as good or
941 bad::
944 bad::
942
945
943 hg bisect --good
946 hg bisect --good
944 hg bisect --bad
947 hg bisect --bad
945
948
946 - mark the current revision, or a known revision, to be skipped (e.g. if
949 - mark the current revision, or a known revision, to be skipped (e.g. if
947 that revision is not usable because of another issue)::
950 that revision is not usable because of another issue)::
948
951
949 hg bisect --skip
952 hg bisect --skip
950 hg bisect --skip 23
953 hg bisect --skip 23
951
954
952 - skip all revisions that do not touch directories ``foo`` or ``bar``::
955 - skip all revisions that do not touch directories ``foo`` or ``bar``::
953
956
954 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
957 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
955
958
956 - forget the current bisection::
959 - forget the current bisection::
957
960
958 hg bisect --reset
961 hg bisect --reset
959
962
960 - use 'make && make tests' to automatically find the first broken
963 - use 'make && make tests' to automatically find the first broken
961 revision::
964 revision::
962
965
963 hg bisect --reset
966 hg bisect --reset
964 hg bisect --bad 34
967 hg bisect --bad 34
965 hg bisect --good 12
968 hg bisect --good 12
966 hg bisect --command "make && make tests"
969 hg bisect --command "make && make tests"
967
970
968 - see all changesets whose states are already known in the current
971 - see all changesets whose states are already known in the current
969 bisection::
972 bisection::
970
973
971 hg log -r "bisect(pruned)"
974 hg log -r "bisect(pruned)"
972
975
973 - see the changeset currently being bisected (especially useful
976 - see the changeset currently being bisected (especially useful
974 if running with -U/--noupdate)::
977 if running with -U/--noupdate)::
975
978
976 hg log -r "bisect(current)"
979 hg log -r "bisect(current)"
977
980
978 - see all changesets that took part in the current bisection::
981 - see all changesets that took part in the current bisection::
979
982
980 hg log -r "bisect(range)"
983 hg log -r "bisect(range)"
981
984
982 - you can even get a nice graph::
985 - you can even get a nice graph::
983
986
984 hg log --graph -r "bisect(range)"
987 hg log --graph -r "bisect(range)"
985
988
986 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
989 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
987
990
988 Returns 0 on success.
991 Returns 0 on success.
989 """
992 """
990 # backward compatibility
993 # backward compatibility
991 if rev in b"good bad reset init".split():
994 if rev in b"good bad reset init".split():
992 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
995 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
993 cmd, rev, extra = rev, extra, None
996 cmd, rev, extra = rev, extra, None
994 if cmd == b"good":
997 if cmd == b"good":
995 good = True
998 good = True
996 elif cmd == b"bad":
999 elif cmd == b"bad":
997 bad = True
1000 bad = True
998 else:
1001 else:
999 reset = True
1002 reset = True
1000 elif extra:
1003 elif extra:
1001 raise error.Abort(_(b'incompatible arguments'))
1004 raise error.Abort(_(b'incompatible arguments'))
1002
1005
1003 incompatibles = {
1006 incompatibles = {
1004 b'--bad': bad,
1007 b'--bad': bad,
1005 b'--command': bool(command),
1008 b'--command': bool(command),
1006 b'--extend': extend,
1009 b'--extend': extend,
1007 b'--good': good,
1010 b'--good': good,
1008 b'--reset': reset,
1011 b'--reset': reset,
1009 b'--skip': skip,
1012 b'--skip': skip,
1010 }
1013 }
1011
1014
1012 enabled = [x for x in incompatibles if incompatibles[x]]
1015 enabled = [x for x in incompatibles if incompatibles[x]]
1013
1016
1014 if len(enabled) > 1:
1017 if len(enabled) > 1:
1015 raise error.Abort(
1018 raise error.Abort(
1016 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1019 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1017 )
1020 )
1018
1021
1019 if reset:
1022 if reset:
1020 hbisect.resetstate(repo)
1023 hbisect.resetstate(repo)
1021 return
1024 return
1022
1025
1023 state = hbisect.load_state(repo)
1026 state = hbisect.load_state(repo)
1024
1027
1025 # update state
1028 # update state
1026 if good or bad or skip:
1029 if good or bad or skip:
1027 if rev:
1030 if rev:
1028 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1031 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1029 else:
1032 else:
1030 nodes = [repo.lookup(b'.')]
1033 nodes = [repo.lookup(b'.')]
1031 if good:
1034 if good:
1032 state[b'good'] += nodes
1035 state[b'good'] += nodes
1033 elif bad:
1036 elif bad:
1034 state[b'bad'] += nodes
1037 state[b'bad'] += nodes
1035 elif skip:
1038 elif skip:
1036 state[b'skip'] += nodes
1039 state[b'skip'] += nodes
1037 hbisect.save_state(repo, state)
1040 hbisect.save_state(repo, state)
1038 if not (state[b'good'] and state[b'bad']):
1041 if not (state[b'good'] and state[b'bad']):
1039 return
1042 return
1040
1043
1041 def mayupdate(repo, node, show_stats=True):
1044 def mayupdate(repo, node, show_stats=True):
1042 """common used update sequence"""
1045 """common used update sequence"""
1043 if noupdate:
1046 if noupdate:
1044 return
1047 return
1045 cmdutil.checkunfinished(repo)
1048 cmdutil.checkunfinished(repo)
1046 cmdutil.bailifchanged(repo)
1049 cmdutil.bailifchanged(repo)
1047 return hg.clean(repo, node, show_stats=show_stats)
1050 return hg.clean(repo, node, show_stats=show_stats)
1048
1051
1049 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1052 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1050
1053
1051 if command:
1054 if command:
1052 changesets = 1
1055 changesets = 1
1053 if noupdate:
1056 if noupdate:
1054 try:
1057 try:
1055 node = state[b'current'][0]
1058 node = state[b'current'][0]
1056 except LookupError:
1059 except LookupError:
1057 raise error.Abort(
1060 raise error.Abort(
1058 _(
1061 _(
1059 b'current bisect revision is unknown - '
1062 b'current bisect revision is unknown - '
1060 b'start a new bisect to fix'
1063 b'start a new bisect to fix'
1061 )
1064 )
1062 )
1065 )
1063 else:
1066 else:
1064 node, p2 = repo.dirstate.parents()
1067 node, p2 = repo.dirstate.parents()
1065 if p2 != nullid:
1068 if p2 != nullid:
1066 raise error.Abort(_(b'current bisect revision is a merge'))
1069 raise error.Abort(_(b'current bisect revision is a merge'))
1067 if rev:
1070 if rev:
1068 node = repo[scmutil.revsingle(repo, rev, node)].node()
1071 node = repo[scmutil.revsingle(repo, rev, node)].node()
1069 with hbisect.restore_state(repo, state, node):
1072 with hbisect.restore_state(repo, state, node):
1070 while changesets:
1073 while changesets:
1071 # update state
1074 # update state
1072 state[b'current'] = [node]
1075 state[b'current'] = [node]
1073 hbisect.save_state(repo, state)
1076 hbisect.save_state(repo, state)
1074 status = ui.system(
1077 status = ui.system(
1075 command,
1078 command,
1076 environ={b'HG_NODE': hex(node)},
1079 environ={b'HG_NODE': hex(node)},
1077 blockedtag=b'bisect_check',
1080 blockedtag=b'bisect_check',
1078 )
1081 )
1079 if status == 125:
1082 if status == 125:
1080 transition = b"skip"
1083 transition = b"skip"
1081 elif status == 0:
1084 elif status == 0:
1082 transition = b"good"
1085 transition = b"good"
1083 # status < 0 means process was killed
1086 # status < 0 means process was killed
1084 elif status == 127:
1087 elif status == 127:
1085 raise error.Abort(_(b"failed to execute %s") % command)
1088 raise error.Abort(_(b"failed to execute %s") % command)
1086 elif status < 0:
1089 elif status < 0:
1087 raise error.Abort(_(b"%s killed") % command)
1090 raise error.Abort(_(b"%s killed") % command)
1088 else:
1091 else:
1089 transition = b"bad"
1092 transition = b"bad"
1090 state[transition].append(node)
1093 state[transition].append(node)
1091 ctx = repo[node]
1094 ctx = repo[node]
1092 ui.status(
1095 ui.status(
1093 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1096 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1094 )
1097 )
1095 hbisect.checkstate(state)
1098 hbisect.checkstate(state)
1096 # bisect
1099 # bisect
1097 nodes, changesets, bgood = hbisect.bisect(repo, state)
1100 nodes, changesets, bgood = hbisect.bisect(repo, state)
1098 # update to next check
1101 # update to next check
1099 node = nodes[0]
1102 node = nodes[0]
1100 mayupdate(repo, node, show_stats=False)
1103 mayupdate(repo, node, show_stats=False)
1101 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1104 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1102 return
1105 return
1103
1106
1104 hbisect.checkstate(state)
1107 hbisect.checkstate(state)
1105
1108
1106 # actually bisect
1109 # actually bisect
1107 nodes, changesets, good = hbisect.bisect(repo, state)
1110 nodes, changesets, good = hbisect.bisect(repo, state)
1108 if extend:
1111 if extend:
1109 if not changesets:
1112 if not changesets:
1110 extendnode = hbisect.extendrange(repo, state, nodes, good)
1113 extendnode = hbisect.extendrange(repo, state, nodes, good)
1111 if extendnode is not None:
1114 if extendnode is not None:
1112 ui.write(
1115 ui.write(
1113 _(b"Extending search to changeset %d:%s\n")
1116 _(b"Extending search to changeset %d:%s\n")
1114 % (extendnode.rev(), extendnode)
1117 % (extendnode.rev(), extendnode)
1115 )
1118 )
1116 state[b'current'] = [extendnode.node()]
1119 state[b'current'] = [extendnode.node()]
1117 hbisect.save_state(repo, state)
1120 hbisect.save_state(repo, state)
1118 return mayupdate(repo, extendnode.node())
1121 return mayupdate(repo, extendnode.node())
1119 raise error.Abort(_(b"nothing to extend"))
1122 raise error.Abort(_(b"nothing to extend"))
1120
1123
1121 if changesets == 0:
1124 if changesets == 0:
1122 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1125 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1123 else:
1126 else:
1124 assert len(nodes) == 1 # only a single node can be tested next
1127 assert len(nodes) == 1 # only a single node can be tested next
1125 node = nodes[0]
1128 node = nodes[0]
1126 # compute the approximate number of remaining tests
1129 # compute the approximate number of remaining tests
1127 tests, size = 0, 2
1130 tests, size = 0, 2
1128 while size <= changesets:
1131 while size <= changesets:
1129 tests, size = tests + 1, size * 2
1132 tests, size = tests + 1, size * 2
1130 rev = repo.changelog.rev(node)
1133 rev = repo.changelog.rev(node)
1131 ui.write(
1134 ui.write(
1132 _(
1135 _(
1133 b"Testing changeset %d:%s "
1136 b"Testing changeset %d:%s "
1134 b"(%d changesets remaining, ~%d tests)\n"
1137 b"(%d changesets remaining, ~%d tests)\n"
1135 )
1138 )
1136 % (rev, short(node), changesets, tests)
1139 % (rev, short(node), changesets, tests)
1137 )
1140 )
1138 state[b'current'] = [node]
1141 state[b'current'] = [node]
1139 hbisect.save_state(repo, state)
1142 hbisect.save_state(repo, state)
1140 return mayupdate(repo, node)
1143 return mayupdate(repo, node)
1141
1144
1142
1145
1143 @command(
1146 @command(
1144 b'bookmarks|bookmark',
1147 b'bookmarks|bookmark',
1145 [
1148 [
1146 (b'f', b'force', False, _(b'force')),
1149 (b'f', b'force', False, _(b'force')),
1147 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1150 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1148 (b'd', b'delete', False, _(b'delete a given bookmark')),
1151 (b'd', b'delete', False, _(b'delete a given bookmark')),
1149 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1152 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1150 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1153 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1151 (b'l', b'list', False, _(b'list existing bookmarks')),
1154 (b'l', b'list', False, _(b'list existing bookmarks')),
1152 ]
1155 ]
1153 + formatteropts,
1156 + formatteropts,
1154 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1157 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1155 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1158 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1156 )
1159 )
1157 def bookmark(ui, repo, *names, **opts):
1160 def bookmark(ui, repo, *names, **opts):
1158 '''create a new bookmark or list existing bookmarks
1161 '''create a new bookmark or list existing bookmarks
1159
1162
1160 Bookmarks are labels on changesets to help track lines of development.
1163 Bookmarks are labels on changesets to help track lines of development.
1161 Bookmarks are unversioned and can be moved, renamed and deleted.
1164 Bookmarks are unversioned and can be moved, renamed and deleted.
1162 Deleting or moving a bookmark has no effect on the associated changesets.
1165 Deleting or moving a bookmark has no effect on the associated changesets.
1163
1166
1164 Creating or updating to a bookmark causes it to be marked as 'active'.
1167 Creating or updating to a bookmark causes it to be marked as 'active'.
1165 The active bookmark is indicated with a '*'.
1168 The active bookmark is indicated with a '*'.
1166 When a commit is made, the active bookmark will advance to the new commit.
1169 When a commit is made, the active bookmark will advance to the new commit.
1167 A plain :hg:`update` will also advance an active bookmark, if possible.
1170 A plain :hg:`update` will also advance an active bookmark, if possible.
1168 Updating away from a bookmark will cause it to be deactivated.
1171 Updating away from a bookmark will cause it to be deactivated.
1169
1172
1170 Bookmarks can be pushed and pulled between repositories (see
1173 Bookmarks can be pushed and pulled between repositories (see
1171 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1174 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1172 diverged, a new 'divergent bookmark' of the form 'name@path' will
1175 diverged, a new 'divergent bookmark' of the form 'name@path' will
1173 be created. Using :hg:`merge` will resolve the divergence.
1176 be created. Using :hg:`merge` will resolve the divergence.
1174
1177
1175 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1178 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1176 the active bookmark's name.
1179 the active bookmark's name.
1177
1180
1178 A bookmark named '@' has the special property that :hg:`clone` will
1181 A bookmark named '@' has the special property that :hg:`clone` will
1179 check it out by default if it exists.
1182 check it out by default if it exists.
1180
1183
1181 .. container:: verbose
1184 .. container:: verbose
1182
1185
1183 Template:
1186 Template:
1184
1187
1185 The following keywords are supported in addition to the common template
1188 The following keywords are supported in addition to the common template
1186 keywords and functions such as ``{bookmark}``. See also
1189 keywords and functions such as ``{bookmark}``. See also
1187 :hg:`help templates`.
1190 :hg:`help templates`.
1188
1191
1189 :active: Boolean. True if the bookmark is active.
1192 :active: Boolean. True if the bookmark is active.
1190
1193
1191 Examples:
1194 Examples:
1192
1195
1193 - create an active bookmark for a new line of development::
1196 - create an active bookmark for a new line of development::
1194
1197
1195 hg book new-feature
1198 hg book new-feature
1196
1199
1197 - create an inactive bookmark as a place marker::
1200 - create an inactive bookmark as a place marker::
1198
1201
1199 hg book -i reviewed
1202 hg book -i reviewed
1200
1203
1201 - create an inactive bookmark on another changeset::
1204 - create an inactive bookmark on another changeset::
1202
1205
1203 hg book -r .^ tested
1206 hg book -r .^ tested
1204
1207
1205 - rename bookmark turkey to dinner::
1208 - rename bookmark turkey to dinner::
1206
1209
1207 hg book -m turkey dinner
1210 hg book -m turkey dinner
1208
1211
1209 - move the '@' bookmark from another branch::
1212 - move the '@' bookmark from another branch::
1210
1213
1211 hg book -f @
1214 hg book -f @
1212
1215
1213 - print only the active bookmark name::
1216 - print only the active bookmark name::
1214
1217
1215 hg book -ql .
1218 hg book -ql .
1216 '''
1219 '''
1217 opts = pycompat.byteskwargs(opts)
1220 opts = pycompat.byteskwargs(opts)
1218 force = opts.get(b'force')
1221 force = opts.get(b'force')
1219 rev = opts.get(b'rev')
1222 rev = opts.get(b'rev')
1220 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1223 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1221
1224
1222 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1225 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1223 if action:
1226 if action:
1224 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1227 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1225 elif names or rev:
1228 elif names or rev:
1226 action = b'add'
1229 action = b'add'
1227 elif inactive:
1230 elif inactive:
1228 action = b'inactive' # meaning deactivate
1231 action = b'inactive' # meaning deactivate
1229 else:
1232 else:
1230 action = b'list'
1233 action = b'list'
1231
1234
1232 cmdutil.check_incompatible_arguments(
1235 cmdutil.check_incompatible_arguments(
1233 opts, b'inactive', [b'delete', b'list']
1236 opts, b'inactive', [b'delete', b'list']
1234 )
1237 )
1235 if not names and action in {b'add', b'delete'}:
1238 if not names and action in {b'add', b'delete'}:
1236 raise error.Abort(_(b"bookmark name required"))
1239 raise error.Abort(_(b"bookmark name required"))
1237
1240
1238 if action in {b'add', b'delete', b'rename', b'inactive'}:
1241 if action in {b'add', b'delete', b'rename', b'inactive'}:
1239 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1242 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1240 if action == b'delete':
1243 if action == b'delete':
1241 names = pycompat.maplist(repo._bookmarks.expandname, names)
1244 names = pycompat.maplist(repo._bookmarks.expandname, names)
1242 bookmarks.delete(repo, tr, names)
1245 bookmarks.delete(repo, tr, names)
1243 elif action == b'rename':
1246 elif action == b'rename':
1244 if not names:
1247 if not names:
1245 raise error.Abort(_(b"new bookmark name required"))
1248 raise error.Abort(_(b"new bookmark name required"))
1246 elif len(names) > 1:
1249 elif len(names) > 1:
1247 raise error.Abort(_(b"only one new bookmark name allowed"))
1250 raise error.Abort(_(b"only one new bookmark name allowed"))
1248 oldname = repo._bookmarks.expandname(opts[b'rename'])
1251 oldname = repo._bookmarks.expandname(opts[b'rename'])
1249 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1252 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1250 elif action == b'add':
1253 elif action == b'add':
1251 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1254 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1252 elif action == b'inactive':
1255 elif action == b'inactive':
1253 if len(repo._bookmarks) == 0:
1256 if len(repo._bookmarks) == 0:
1254 ui.status(_(b"no bookmarks set\n"))
1257 ui.status(_(b"no bookmarks set\n"))
1255 elif not repo._activebookmark:
1258 elif not repo._activebookmark:
1256 ui.status(_(b"no active bookmark\n"))
1259 ui.status(_(b"no active bookmark\n"))
1257 else:
1260 else:
1258 bookmarks.deactivate(repo)
1261 bookmarks.deactivate(repo)
1259 elif action == b'list':
1262 elif action == b'list':
1260 names = pycompat.maplist(repo._bookmarks.expandname, names)
1263 names = pycompat.maplist(repo._bookmarks.expandname, names)
1261 with ui.formatter(b'bookmarks', opts) as fm:
1264 with ui.formatter(b'bookmarks', opts) as fm:
1262 bookmarks.printbookmarks(ui, repo, fm, names)
1265 bookmarks.printbookmarks(ui, repo, fm, names)
1263 else:
1266 else:
1264 raise error.ProgrammingError(b'invalid action: %s' % action)
1267 raise error.ProgrammingError(b'invalid action: %s' % action)
1265
1268
1266
1269
1267 @command(
1270 @command(
1268 b'branch',
1271 b'branch',
1269 [
1272 [
1270 (
1273 (
1271 b'f',
1274 b'f',
1272 b'force',
1275 b'force',
1273 None,
1276 None,
1274 _(b'set branch name even if it shadows an existing branch'),
1277 _(b'set branch name even if it shadows an existing branch'),
1275 ),
1278 ),
1276 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1279 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1277 (
1280 (
1278 b'r',
1281 b'r',
1279 b'rev',
1282 b'rev',
1280 [],
1283 [],
1281 _(b'change branches of the given revs (EXPERIMENTAL)'),
1284 _(b'change branches of the given revs (EXPERIMENTAL)'),
1282 ),
1285 ),
1283 ],
1286 ],
1284 _(b'[-fC] [NAME]'),
1287 _(b'[-fC] [NAME]'),
1285 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1288 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1286 )
1289 )
1287 def branch(ui, repo, label=None, **opts):
1290 def branch(ui, repo, label=None, **opts):
1288 """set or show the current branch name
1291 """set or show the current branch name
1289
1292
1290 .. note::
1293 .. note::
1291
1294
1292 Branch names are permanent and global. Use :hg:`bookmark` to create a
1295 Branch names are permanent and global. Use :hg:`bookmark` to create a
1293 light-weight bookmark instead. See :hg:`help glossary` for more
1296 light-weight bookmark instead. See :hg:`help glossary` for more
1294 information about named branches and bookmarks.
1297 information about named branches and bookmarks.
1295
1298
1296 With no argument, show the current branch name. With one argument,
1299 With no argument, show the current branch name. With one argument,
1297 set the working directory branch name (the branch will not exist
1300 set the working directory branch name (the branch will not exist
1298 in the repository until the next commit). Standard practice
1301 in the repository until the next commit). Standard practice
1299 recommends that primary development take place on the 'default'
1302 recommends that primary development take place on the 'default'
1300 branch.
1303 branch.
1301
1304
1302 Unless -f/--force is specified, branch will not let you set a
1305 Unless -f/--force is specified, branch will not let you set a
1303 branch name that already exists.
1306 branch name that already exists.
1304
1307
1305 Use -C/--clean to reset the working directory branch to that of
1308 Use -C/--clean to reset the working directory branch to that of
1306 the parent of the working directory, negating a previous branch
1309 the parent of the working directory, negating a previous branch
1307 change.
1310 change.
1308
1311
1309 Use the command :hg:`update` to switch to an existing branch. Use
1312 Use the command :hg:`update` to switch to an existing branch. Use
1310 :hg:`commit --close-branch` to mark this branch head as closed.
1313 :hg:`commit --close-branch` to mark this branch head as closed.
1311 When all heads of a branch are closed, the branch will be
1314 When all heads of a branch are closed, the branch will be
1312 considered closed.
1315 considered closed.
1313
1316
1314 Returns 0 on success.
1317 Returns 0 on success.
1315 """
1318 """
1316 opts = pycompat.byteskwargs(opts)
1319 opts = pycompat.byteskwargs(opts)
1317 revs = opts.get(b'rev')
1320 revs = opts.get(b'rev')
1318 if label:
1321 if label:
1319 label = label.strip()
1322 label = label.strip()
1320
1323
1321 if not opts.get(b'clean') and not label:
1324 if not opts.get(b'clean') and not label:
1322 if revs:
1325 if revs:
1323 raise error.Abort(_(b"no branch name specified for the revisions"))
1326 raise error.Abort(_(b"no branch name specified for the revisions"))
1324 ui.write(b"%s\n" % repo.dirstate.branch())
1327 ui.write(b"%s\n" % repo.dirstate.branch())
1325 return
1328 return
1326
1329
1327 with repo.wlock():
1330 with repo.wlock():
1328 if opts.get(b'clean'):
1331 if opts.get(b'clean'):
1329 label = repo[b'.'].branch()
1332 label = repo[b'.'].branch()
1330 repo.dirstate.setbranch(label)
1333 repo.dirstate.setbranch(label)
1331 ui.status(_(b'reset working directory to branch %s\n') % label)
1334 ui.status(_(b'reset working directory to branch %s\n') % label)
1332 elif label:
1335 elif label:
1333
1336
1334 scmutil.checknewlabel(repo, label, b'branch')
1337 scmutil.checknewlabel(repo, label, b'branch')
1335 if revs:
1338 if revs:
1336 return cmdutil.changebranch(ui, repo, revs, label, opts)
1339 return cmdutil.changebranch(ui, repo, revs, label, opts)
1337
1340
1338 if not opts.get(b'force') and label in repo.branchmap():
1341 if not opts.get(b'force') and label in repo.branchmap():
1339 if label not in [p.branch() for p in repo[None].parents()]:
1342 if label not in [p.branch() for p in repo[None].parents()]:
1340 raise error.Abort(
1343 raise error.Abort(
1341 _(b'a branch of the same name already exists'),
1344 _(b'a branch of the same name already exists'),
1342 # i18n: "it" refers to an existing branch
1345 # i18n: "it" refers to an existing branch
1343 hint=_(b"use 'hg update' to switch to it"),
1346 hint=_(b"use 'hg update' to switch to it"),
1344 )
1347 )
1345
1348
1346 repo.dirstate.setbranch(label)
1349 repo.dirstate.setbranch(label)
1347 ui.status(_(b'marked working directory as branch %s\n') % label)
1350 ui.status(_(b'marked working directory as branch %s\n') % label)
1348
1351
1349 # find any open named branches aside from default
1352 # find any open named branches aside from default
1350 for n, h, t, c in repo.branchmap().iterbranches():
1353 for n, h, t, c in repo.branchmap().iterbranches():
1351 if n != b"default" and not c:
1354 if n != b"default" and not c:
1352 return 0
1355 return 0
1353 ui.status(
1356 ui.status(
1354 _(
1357 _(
1355 b'(branches are permanent and global, '
1358 b'(branches are permanent and global, '
1356 b'did you want a bookmark?)\n'
1359 b'did you want a bookmark?)\n'
1357 )
1360 )
1358 )
1361 )
1359
1362
1360
1363
1361 @command(
1364 @command(
1362 b'branches',
1365 b'branches',
1363 [
1366 [
1364 (
1367 (
1365 b'a',
1368 b'a',
1366 b'active',
1369 b'active',
1367 False,
1370 False,
1368 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1371 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1369 ),
1372 ),
1370 (b'c', b'closed', False, _(b'show normal and closed branches')),
1373 (b'c', b'closed', False, _(b'show normal and closed branches')),
1371 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1374 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1372 ]
1375 ]
1373 + formatteropts,
1376 + formatteropts,
1374 _(b'[-c]'),
1377 _(b'[-c]'),
1375 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1378 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1376 intents={INTENT_READONLY},
1379 intents={INTENT_READONLY},
1377 )
1380 )
1378 def branches(ui, repo, active=False, closed=False, **opts):
1381 def branches(ui, repo, active=False, closed=False, **opts):
1379 """list repository named branches
1382 """list repository named branches
1380
1383
1381 List the repository's named branches, indicating which ones are
1384 List the repository's named branches, indicating which ones are
1382 inactive. If -c/--closed is specified, also list branches which have
1385 inactive. If -c/--closed is specified, also list branches which have
1383 been marked closed (see :hg:`commit --close-branch`).
1386 been marked closed (see :hg:`commit --close-branch`).
1384
1387
1385 Use the command :hg:`update` to switch to an existing branch.
1388 Use the command :hg:`update` to switch to an existing branch.
1386
1389
1387 .. container:: verbose
1390 .. container:: verbose
1388
1391
1389 Template:
1392 Template:
1390
1393
1391 The following keywords are supported in addition to the common template
1394 The following keywords are supported in addition to the common template
1392 keywords and functions such as ``{branch}``. See also
1395 keywords and functions such as ``{branch}``. See also
1393 :hg:`help templates`.
1396 :hg:`help templates`.
1394
1397
1395 :active: Boolean. True if the branch is active.
1398 :active: Boolean. True if the branch is active.
1396 :closed: Boolean. True if the branch is closed.
1399 :closed: Boolean. True if the branch is closed.
1397 :current: Boolean. True if it is the current branch.
1400 :current: Boolean. True if it is the current branch.
1398
1401
1399 Returns 0.
1402 Returns 0.
1400 """
1403 """
1401
1404
1402 opts = pycompat.byteskwargs(opts)
1405 opts = pycompat.byteskwargs(opts)
1403 revs = opts.get(b'rev')
1406 revs = opts.get(b'rev')
1404 selectedbranches = None
1407 selectedbranches = None
1405 if revs:
1408 if revs:
1406 revs = scmutil.revrange(repo, revs)
1409 revs = scmutil.revrange(repo, revs)
1407 getbi = repo.revbranchcache().branchinfo
1410 getbi = repo.revbranchcache().branchinfo
1408 selectedbranches = {getbi(r)[0] for r in revs}
1411 selectedbranches = {getbi(r)[0] for r in revs}
1409
1412
1410 ui.pager(b'branches')
1413 ui.pager(b'branches')
1411 fm = ui.formatter(b'branches', opts)
1414 fm = ui.formatter(b'branches', opts)
1412 hexfunc = fm.hexfunc
1415 hexfunc = fm.hexfunc
1413
1416
1414 allheads = set(repo.heads())
1417 allheads = set(repo.heads())
1415 branches = []
1418 branches = []
1416 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1419 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1417 if selectedbranches is not None and tag not in selectedbranches:
1420 if selectedbranches is not None and tag not in selectedbranches:
1418 continue
1421 continue
1419 isactive = False
1422 isactive = False
1420 if not isclosed:
1423 if not isclosed:
1421 openheads = set(repo.branchmap().iteropen(heads))
1424 openheads = set(repo.branchmap().iteropen(heads))
1422 isactive = bool(openheads & allheads)
1425 isactive = bool(openheads & allheads)
1423 branches.append((tag, repo[tip], isactive, not isclosed))
1426 branches.append((tag, repo[tip], isactive, not isclosed))
1424 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1427 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1425
1428
1426 for tag, ctx, isactive, isopen in branches:
1429 for tag, ctx, isactive, isopen in branches:
1427 if active and not isactive:
1430 if active and not isactive:
1428 continue
1431 continue
1429 if isactive:
1432 if isactive:
1430 label = b'branches.active'
1433 label = b'branches.active'
1431 notice = b''
1434 notice = b''
1432 elif not isopen:
1435 elif not isopen:
1433 if not closed:
1436 if not closed:
1434 continue
1437 continue
1435 label = b'branches.closed'
1438 label = b'branches.closed'
1436 notice = _(b' (closed)')
1439 notice = _(b' (closed)')
1437 else:
1440 else:
1438 label = b'branches.inactive'
1441 label = b'branches.inactive'
1439 notice = _(b' (inactive)')
1442 notice = _(b' (inactive)')
1440 current = tag == repo.dirstate.branch()
1443 current = tag == repo.dirstate.branch()
1441 if current:
1444 if current:
1442 label = b'branches.current'
1445 label = b'branches.current'
1443
1446
1444 fm.startitem()
1447 fm.startitem()
1445 fm.write(b'branch', b'%s', tag, label=label)
1448 fm.write(b'branch', b'%s', tag, label=label)
1446 rev = ctx.rev()
1449 rev = ctx.rev()
1447 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1450 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1448 fmt = b' ' * padsize + b' %d:%s'
1451 fmt = b' ' * padsize + b' %d:%s'
1449 fm.condwrite(
1452 fm.condwrite(
1450 not ui.quiet,
1453 not ui.quiet,
1451 b'rev node',
1454 b'rev node',
1452 fmt,
1455 fmt,
1453 rev,
1456 rev,
1454 hexfunc(ctx.node()),
1457 hexfunc(ctx.node()),
1455 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1458 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1456 )
1459 )
1457 fm.context(ctx=ctx)
1460 fm.context(ctx=ctx)
1458 fm.data(active=isactive, closed=not isopen, current=current)
1461 fm.data(active=isactive, closed=not isopen, current=current)
1459 if not ui.quiet:
1462 if not ui.quiet:
1460 fm.plain(notice)
1463 fm.plain(notice)
1461 fm.plain(b'\n')
1464 fm.plain(b'\n')
1462 fm.end()
1465 fm.end()
1463
1466
1464
1467
1465 @command(
1468 @command(
1466 b'bundle',
1469 b'bundle',
1467 [
1470 [
1468 (
1471 (
1469 b'f',
1472 b'f',
1470 b'force',
1473 b'force',
1471 None,
1474 None,
1472 _(b'run even when the destination is unrelated'),
1475 _(b'run even when the destination is unrelated'),
1473 ),
1476 ),
1474 (
1477 (
1475 b'r',
1478 b'r',
1476 b'rev',
1479 b'rev',
1477 [],
1480 [],
1478 _(b'a changeset intended to be added to the destination'),
1481 _(b'a changeset intended to be added to the destination'),
1479 _(b'REV'),
1482 _(b'REV'),
1480 ),
1483 ),
1481 (
1484 (
1482 b'b',
1485 b'b',
1483 b'branch',
1486 b'branch',
1484 [],
1487 [],
1485 _(b'a specific branch you would like to bundle'),
1488 _(b'a specific branch you would like to bundle'),
1486 _(b'BRANCH'),
1489 _(b'BRANCH'),
1487 ),
1490 ),
1488 (
1491 (
1489 b'',
1492 b'',
1490 b'base',
1493 b'base',
1491 [],
1494 [],
1492 _(b'a base changeset assumed to be available at the destination'),
1495 _(b'a base changeset assumed to be available at the destination'),
1493 _(b'REV'),
1496 _(b'REV'),
1494 ),
1497 ),
1495 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1498 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1496 (
1499 (
1497 b't',
1500 b't',
1498 b'type',
1501 b'type',
1499 b'bzip2',
1502 b'bzip2',
1500 _(b'bundle compression type to use'),
1503 _(b'bundle compression type to use'),
1501 _(b'TYPE'),
1504 _(b'TYPE'),
1502 ),
1505 ),
1503 ]
1506 ]
1504 + remoteopts,
1507 + remoteopts,
1505 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1508 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1506 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1509 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1507 )
1510 )
1508 def bundle(ui, repo, fname, dest=None, **opts):
1511 def bundle(ui, repo, fname, dest=None, **opts):
1509 """create a bundle file
1512 """create a bundle file
1510
1513
1511 Generate a bundle file containing data to be transferred to another
1514 Generate a bundle file containing data to be transferred to another
1512 repository.
1515 repository.
1513
1516
1514 To create a bundle containing all changesets, use -a/--all
1517 To create a bundle containing all changesets, use -a/--all
1515 (or --base null). Otherwise, hg assumes the destination will have
1518 (or --base null). Otherwise, hg assumes the destination will have
1516 all the nodes you specify with --base parameters. Otherwise, hg
1519 all the nodes you specify with --base parameters. Otherwise, hg
1517 will assume the repository has all the nodes in destination, or
1520 will assume the repository has all the nodes in destination, or
1518 default-push/default if no destination is specified, where destination
1521 default-push/default if no destination is specified, where destination
1519 is the repository you provide through DEST option.
1522 is the repository you provide through DEST option.
1520
1523
1521 You can change bundle format with the -t/--type option. See
1524 You can change bundle format with the -t/--type option. See
1522 :hg:`help bundlespec` for documentation on this format. By default,
1525 :hg:`help bundlespec` for documentation on this format. By default,
1523 the most appropriate format is used and compression defaults to
1526 the most appropriate format is used and compression defaults to
1524 bzip2.
1527 bzip2.
1525
1528
1526 The bundle file can then be transferred using conventional means
1529 The bundle file can then be transferred using conventional means
1527 and applied to another repository with the unbundle or pull
1530 and applied to another repository with the unbundle or pull
1528 command. This is useful when direct push and pull are not
1531 command. This is useful when direct push and pull are not
1529 available or when exporting an entire repository is undesirable.
1532 available or when exporting an entire repository is undesirable.
1530
1533
1531 Applying bundles preserves all changeset contents including
1534 Applying bundles preserves all changeset contents including
1532 permissions, copy/rename information, and revision history.
1535 permissions, copy/rename information, and revision history.
1533
1536
1534 Returns 0 on success, 1 if no changes found.
1537 Returns 0 on success, 1 if no changes found.
1535 """
1538 """
1536 opts = pycompat.byteskwargs(opts)
1539 opts = pycompat.byteskwargs(opts)
1537 revs = None
1540 revs = None
1538 if b'rev' in opts:
1541 if b'rev' in opts:
1539 revstrings = opts[b'rev']
1542 revstrings = opts[b'rev']
1540 revs = scmutil.revrange(repo, revstrings)
1543 revs = scmutil.revrange(repo, revstrings)
1541 if revstrings and not revs:
1544 if revstrings and not revs:
1542 raise error.Abort(_(b'no commits to bundle'))
1545 raise error.Abort(_(b'no commits to bundle'))
1543
1546
1544 bundletype = opts.get(b'type', b'bzip2').lower()
1547 bundletype = opts.get(b'type', b'bzip2').lower()
1545 try:
1548 try:
1546 bundlespec = bundlecaches.parsebundlespec(
1549 bundlespec = bundlecaches.parsebundlespec(
1547 repo, bundletype, strict=False
1550 repo, bundletype, strict=False
1548 )
1551 )
1549 except error.UnsupportedBundleSpecification as e:
1552 except error.UnsupportedBundleSpecification as e:
1550 raise error.Abort(
1553 raise error.Abort(
1551 pycompat.bytestr(e),
1554 pycompat.bytestr(e),
1552 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1555 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1553 )
1556 )
1554 cgversion = bundlespec.contentopts[b"cg.version"]
1557 cgversion = bundlespec.contentopts[b"cg.version"]
1555
1558
1556 # Packed bundles are a pseudo bundle format for now.
1559 # Packed bundles are a pseudo bundle format for now.
1557 if cgversion == b's1':
1560 if cgversion == b's1':
1558 raise error.Abort(
1561 raise error.Abort(
1559 _(b'packed bundles cannot be produced by "hg bundle"'),
1562 _(b'packed bundles cannot be produced by "hg bundle"'),
1560 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1563 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1561 )
1564 )
1562
1565
1563 if opts.get(b'all'):
1566 if opts.get(b'all'):
1564 if dest:
1567 if dest:
1565 raise error.Abort(
1568 raise error.Abort(
1566 _(b"--all is incompatible with specifying a destination")
1569 _(b"--all is incompatible with specifying a destination")
1567 )
1570 )
1568 if opts.get(b'base'):
1571 if opts.get(b'base'):
1569 ui.warn(_(b"ignoring --base because --all was specified\n"))
1572 ui.warn(_(b"ignoring --base because --all was specified\n"))
1570 base = [nullrev]
1573 base = [nullrev]
1571 else:
1574 else:
1572 base = scmutil.revrange(repo, opts.get(b'base'))
1575 base = scmutil.revrange(repo, opts.get(b'base'))
1573 if cgversion not in changegroup.supportedoutgoingversions(repo):
1576 if cgversion not in changegroup.supportedoutgoingversions(repo):
1574 raise error.Abort(
1577 raise error.Abort(
1575 _(b"repository does not support bundle version %s") % cgversion
1578 _(b"repository does not support bundle version %s") % cgversion
1576 )
1579 )
1577
1580
1578 if base:
1581 if base:
1579 if dest:
1582 if dest:
1580 raise error.Abort(
1583 raise error.Abort(
1581 _(b"--base is incompatible with specifying a destination")
1584 _(b"--base is incompatible with specifying a destination")
1582 )
1585 )
1583 common = [repo[rev].node() for rev in base]
1586 common = [repo[rev].node() for rev in base]
1584 heads = [repo[r].node() for r in revs] if revs else None
1587 heads = [repo[r].node() for r in revs] if revs else None
1585 outgoing = discovery.outgoing(repo, common, heads)
1588 outgoing = discovery.outgoing(repo, common, heads)
1586 else:
1589 else:
1587 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1590 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1588 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1591 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1589 other = hg.peer(repo, opts, dest)
1592 other = hg.peer(repo, opts, dest)
1590 revs = [repo[r].hex() for r in revs]
1593 revs = [repo[r].hex() for r in revs]
1591 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1594 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1592 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1595 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1593 outgoing = discovery.findcommonoutgoing(
1596 outgoing = discovery.findcommonoutgoing(
1594 repo,
1597 repo,
1595 other,
1598 other,
1596 onlyheads=heads,
1599 onlyheads=heads,
1597 force=opts.get(b'force'),
1600 force=opts.get(b'force'),
1598 portable=True,
1601 portable=True,
1599 )
1602 )
1600
1603
1601 if not outgoing.missing:
1604 if not outgoing.missing:
1602 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1605 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1603 return 1
1606 return 1
1604
1607
1605 if cgversion == b'01': # bundle1
1608 if cgversion == b'01': # bundle1
1606 bversion = b'HG10' + bundlespec.wirecompression
1609 bversion = b'HG10' + bundlespec.wirecompression
1607 bcompression = None
1610 bcompression = None
1608 elif cgversion in (b'02', b'03'):
1611 elif cgversion in (b'02', b'03'):
1609 bversion = b'HG20'
1612 bversion = b'HG20'
1610 bcompression = bundlespec.wirecompression
1613 bcompression = bundlespec.wirecompression
1611 else:
1614 else:
1612 raise error.ProgrammingError(
1615 raise error.ProgrammingError(
1613 b'bundle: unexpected changegroup version %s' % cgversion
1616 b'bundle: unexpected changegroup version %s' % cgversion
1614 )
1617 )
1615
1618
1616 # TODO compression options should be derived from bundlespec parsing.
1619 # TODO compression options should be derived from bundlespec parsing.
1617 # This is a temporary hack to allow adjusting bundle compression
1620 # This is a temporary hack to allow adjusting bundle compression
1618 # level without a) formalizing the bundlespec changes to declare it
1621 # level without a) formalizing the bundlespec changes to declare it
1619 # b) introducing a command flag.
1622 # b) introducing a command flag.
1620 compopts = {}
1623 compopts = {}
1621 complevel = ui.configint(
1624 complevel = ui.configint(
1622 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1625 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1623 )
1626 )
1624 if complevel is None:
1627 if complevel is None:
1625 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1628 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1626 if complevel is not None:
1629 if complevel is not None:
1627 compopts[b'level'] = complevel
1630 compopts[b'level'] = complevel
1628
1631
1629 # Allow overriding the bundling of obsmarker in phases through
1632 # Allow overriding the bundling of obsmarker in phases through
1630 # configuration while we don't have a bundle version that include them
1633 # configuration while we don't have a bundle version that include them
1631 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1634 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1632 bundlespec.contentopts[b'obsolescence'] = True
1635 bundlespec.contentopts[b'obsolescence'] = True
1633 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1636 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1634 bundlespec.contentopts[b'phases'] = True
1637 bundlespec.contentopts[b'phases'] = True
1635
1638
1636 bundle2.writenewbundle(
1639 bundle2.writenewbundle(
1637 ui,
1640 ui,
1638 repo,
1641 repo,
1639 b'bundle',
1642 b'bundle',
1640 fname,
1643 fname,
1641 bversion,
1644 bversion,
1642 outgoing,
1645 outgoing,
1643 bundlespec.contentopts,
1646 bundlespec.contentopts,
1644 compression=bcompression,
1647 compression=bcompression,
1645 compopts=compopts,
1648 compopts=compopts,
1646 )
1649 )
1647
1650
1648
1651
1649 @command(
1652 @command(
1650 b'cat',
1653 b'cat',
1651 [
1654 [
1652 (
1655 (
1653 b'o',
1656 b'o',
1654 b'output',
1657 b'output',
1655 b'',
1658 b'',
1656 _(b'print output to file with formatted name'),
1659 _(b'print output to file with formatted name'),
1657 _(b'FORMAT'),
1660 _(b'FORMAT'),
1658 ),
1661 ),
1659 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1662 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1660 (b'', b'decode', None, _(b'apply any matching decode filter')),
1663 (b'', b'decode', None, _(b'apply any matching decode filter')),
1661 ]
1664 ]
1662 + walkopts
1665 + walkopts
1663 + formatteropts,
1666 + formatteropts,
1664 _(b'[OPTION]... FILE...'),
1667 _(b'[OPTION]... FILE...'),
1665 helpcategory=command.CATEGORY_FILE_CONTENTS,
1668 helpcategory=command.CATEGORY_FILE_CONTENTS,
1666 inferrepo=True,
1669 inferrepo=True,
1667 intents={INTENT_READONLY},
1670 intents={INTENT_READONLY},
1668 )
1671 )
1669 def cat(ui, repo, file1, *pats, **opts):
1672 def cat(ui, repo, file1, *pats, **opts):
1670 """output the current or given revision of files
1673 """output the current or given revision of files
1671
1674
1672 Print the specified files as they were at the given revision. If
1675 Print the specified files as they were at the given revision. If
1673 no revision is given, the parent of the working directory is used.
1676 no revision is given, the parent of the working directory is used.
1674
1677
1675 Output may be to a file, in which case the name of the file is
1678 Output may be to a file, in which case the name of the file is
1676 given using a template string. See :hg:`help templates`. In addition
1679 given using a template string. See :hg:`help templates`. In addition
1677 to the common template keywords, the following formatting rules are
1680 to the common template keywords, the following formatting rules are
1678 supported:
1681 supported:
1679
1682
1680 :``%%``: literal "%" character
1683 :``%%``: literal "%" character
1681 :``%s``: basename of file being printed
1684 :``%s``: basename of file being printed
1682 :``%d``: dirname of file being printed, or '.' if in repository root
1685 :``%d``: dirname of file being printed, or '.' if in repository root
1683 :``%p``: root-relative path name of file being printed
1686 :``%p``: root-relative path name of file being printed
1684 :``%H``: changeset hash (40 hexadecimal digits)
1687 :``%H``: changeset hash (40 hexadecimal digits)
1685 :``%R``: changeset revision number
1688 :``%R``: changeset revision number
1686 :``%h``: short-form changeset hash (12 hexadecimal digits)
1689 :``%h``: short-form changeset hash (12 hexadecimal digits)
1687 :``%r``: zero-padded changeset revision number
1690 :``%r``: zero-padded changeset revision number
1688 :``%b``: basename of the exporting repository
1691 :``%b``: basename of the exporting repository
1689 :``\\``: literal "\\" character
1692 :``\\``: literal "\\" character
1690
1693
1691 .. container:: verbose
1694 .. container:: verbose
1692
1695
1693 Template:
1696 Template:
1694
1697
1695 The following keywords are supported in addition to the common template
1698 The following keywords are supported in addition to the common template
1696 keywords and functions. See also :hg:`help templates`.
1699 keywords and functions. See also :hg:`help templates`.
1697
1700
1698 :data: String. File content.
1701 :data: String. File content.
1699 :path: String. Repository-absolute path of the file.
1702 :path: String. Repository-absolute path of the file.
1700
1703
1701 Returns 0 on success.
1704 Returns 0 on success.
1702 """
1705 """
1703 opts = pycompat.byteskwargs(opts)
1706 opts = pycompat.byteskwargs(opts)
1704 rev = opts.get(b'rev')
1707 rev = opts.get(b'rev')
1705 if rev:
1708 if rev:
1706 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1709 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1707 ctx = scmutil.revsingle(repo, rev)
1710 ctx = scmutil.revsingle(repo, rev)
1708 m = scmutil.match(ctx, (file1,) + pats, opts)
1711 m = scmutil.match(ctx, (file1,) + pats, opts)
1709 fntemplate = opts.pop(b'output', b'')
1712 fntemplate = opts.pop(b'output', b'')
1710 if cmdutil.isstdiofilename(fntemplate):
1713 if cmdutil.isstdiofilename(fntemplate):
1711 fntemplate = b''
1714 fntemplate = b''
1712
1715
1713 if fntemplate:
1716 if fntemplate:
1714 fm = formatter.nullformatter(ui, b'cat', opts)
1717 fm = formatter.nullformatter(ui, b'cat', opts)
1715 else:
1718 else:
1716 ui.pager(b'cat')
1719 ui.pager(b'cat')
1717 fm = ui.formatter(b'cat', opts)
1720 fm = ui.formatter(b'cat', opts)
1718 with fm:
1721 with fm:
1719 return cmdutil.cat(
1722 return cmdutil.cat(
1720 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1723 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1721 )
1724 )
1722
1725
1723
1726
1724 @command(
1727 @command(
1725 b'clone',
1728 b'clone',
1726 [
1729 [
1727 (
1730 (
1728 b'U',
1731 b'U',
1729 b'noupdate',
1732 b'noupdate',
1730 None,
1733 None,
1731 _(
1734 _(
1732 b'the clone will include an empty working '
1735 b'the clone will include an empty working '
1733 b'directory (only a repository)'
1736 b'directory (only a repository)'
1734 ),
1737 ),
1735 ),
1738 ),
1736 (
1739 (
1737 b'u',
1740 b'u',
1738 b'updaterev',
1741 b'updaterev',
1739 b'',
1742 b'',
1740 _(b'revision, tag, or branch to check out'),
1743 _(b'revision, tag, or branch to check out'),
1741 _(b'REV'),
1744 _(b'REV'),
1742 ),
1745 ),
1743 (
1746 (
1744 b'r',
1747 b'r',
1745 b'rev',
1748 b'rev',
1746 [],
1749 [],
1747 _(
1750 _(
1748 b'do not clone everything, but include this changeset'
1751 b'do not clone everything, but include this changeset'
1749 b' and its ancestors'
1752 b' and its ancestors'
1750 ),
1753 ),
1751 _(b'REV'),
1754 _(b'REV'),
1752 ),
1755 ),
1753 (
1756 (
1754 b'b',
1757 b'b',
1755 b'branch',
1758 b'branch',
1756 [],
1759 [],
1757 _(
1760 _(
1758 b'do not clone everything, but include this branch\'s'
1761 b'do not clone everything, but include this branch\'s'
1759 b' changesets and their ancestors'
1762 b' changesets and their ancestors'
1760 ),
1763 ),
1761 _(b'BRANCH'),
1764 _(b'BRANCH'),
1762 ),
1765 ),
1763 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1766 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1764 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1767 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1765 (b'', b'stream', None, _(b'clone with minimal data processing')),
1768 (b'', b'stream', None, _(b'clone with minimal data processing')),
1766 ]
1769 ]
1767 + remoteopts,
1770 + remoteopts,
1768 _(b'[OPTION]... SOURCE [DEST]'),
1771 _(b'[OPTION]... SOURCE [DEST]'),
1769 helpcategory=command.CATEGORY_REPO_CREATION,
1772 helpcategory=command.CATEGORY_REPO_CREATION,
1770 helpbasic=True,
1773 helpbasic=True,
1771 norepo=True,
1774 norepo=True,
1772 )
1775 )
1773 def clone(ui, source, dest=None, **opts):
1776 def clone(ui, source, dest=None, **opts):
1774 """make a copy of an existing repository
1777 """make a copy of an existing repository
1775
1778
1776 Create a copy of an existing repository in a new directory.
1779 Create a copy of an existing repository in a new directory.
1777
1780
1778 If no destination directory name is specified, it defaults to the
1781 If no destination directory name is specified, it defaults to the
1779 basename of the source.
1782 basename of the source.
1780
1783
1781 The location of the source is added to the new repository's
1784 The location of the source is added to the new repository's
1782 ``.hg/hgrc`` file, as the default to be used for future pulls.
1785 ``.hg/hgrc`` file, as the default to be used for future pulls.
1783
1786
1784 Only local paths and ``ssh://`` URLs are supported as
1787 Only local paths and ``ssh://`` URLs are supported as
1785 destinations. For ``ssh://`` destinations, no working directory or
1788 destinations. For ``ssh://`` destinations, no working directory or
1786 ``.hg/hgrc`` will be created on the remote side.
1789 ``.hg/hgrc`` will be created on the remote side.
1787
1790
1788 If the source repository has a bookmark called '@' set, that
1791 If the source repository has a bookmark called '@' set, that
1789 revision will be checked out in the new repository by default.
1792 revision will be checked out in the new repository by default.
1790
1793
1791 To check out a particular version, use -u/--update, or
1794 To check out a particular version, use -u/--update, or
1792 -U/--noupdate to create a clone with no working directory.
1795 -U/--noupdate to create a clone with no working directory.
1793
1796
1794 To pull only a subset of changesets, specify one or more revisions
1797 To pull only a subset of changesets, specify one or more revisions
1795 identifiers with -r/--rev or branches with -b/--branch. The
1798 identifiers with -r/--rev or branches with -b/--branch. The
1796 resulting clone will contain only the specified changesets and
1799 resulting clone will contain only the specified changesets and
1797 their ancestors. These options (or 'clone src#rev dest') imply
1800 their ancestors. These options (or 'clone src#rev dest') imply
1798 --pull, even for local source repositories.
1801 --pull, even for local source repositories.
1799
1802
1800 In normal clone mode, the remote normalizes repository data into a common
1803 In normal clone mode, the remote normalizes repository data into a common
1801 exchange format and the receiving end translates this data into its local
1804 exchange format and the receiving end translates this data into its local
1802 storage format. --stream activates a different clone mode that essentially
1805 storage format. --stream activates a different clone mode that essentially
1803 copies repository files from the remote with minimal data processing. This
1806 copies repository files from the remote with minimal data processing. This
1804 significantly reduces the CPU cost of a clone both remotely and locally.
1807 significantly reduces the CPU cost of a clone both remotely and locally.
1805 However, it often increases the transferred data size by 30-40%. This can
1808 However, it often increases the transferred data size by 30-40%. This can
1806 result in substantially faster clones where I/O throughput is plentiful,
1809 result in substantially faster clones where I/O throughput is plentiful,
1807 especially for larger repositories. A side-effect of --stream clones is
1810 especially for larger repositories. A side-effect of --stream clones is
1808 that storage settings and requirements on the remote are applied locally:
1811 that storage settings and requirements on the remote are applied locally:
1809 a modern client may inherit legacy or inefficient storage used by the
1812 a modern client may inherit legacy or inefficient storage used by the
1810 remote or a legacy Mercurial client may not be able to clone from a
1813 remote or a legacy Mercurial client may not be able to clone from a
1811 modern Mercurial remote.
1814 modern Mercurial remote.
1812
1815
1813 .. note::
1816 .. note::
1814
1817
1815 Specifying a tag will include the tagged changeset but not the
1818 Specifying a tag will include the tagged changeset but not the
1816 changeset containing the tag.
1819 changeset containing the tag.
1817
1820
1818 .. container:: verbose
1821 .. container:: verbose
1819
1822
1820 For efficiency, hardlinks are used for cloning whenever the
1823 For efficiency, hardlinks are used for cloning whenever the
1821 source and destination are on the same filesystem (note this
1824 source and destination are on the same filesystem (note this
1822 applies only to the repository data, not to the working
1825 applies only to the repository data, not to the working
1823 directory). Some filesystems, such as AFS, implement hardlinking
1826 directory). Some filesystems, such as AFS, implement hardlinking
1824 incorrectly, but do not report errors. In these cases, use the
1827 incorrectly, but do not report errors. In these cases, use the
1825 --pull option to avoid hardlinking.
1828 --pull option to avoid hardlinking.
1826
1829
1827 Mercurial will update the working directory to the first applicable
1830 Mercurial will update the working directory to the first applicable
1828 revision from this list:
1831 revision from this list:
1829
1832
1830 a) null if -U or the source repository has no changesets
1833 a) null if -U or the source repository has no changesets
1831 b) if -u . and the source repository is local, the first parent of
1834 b) if -u . and the source repository is local, the first parent of
1832 the source repository's working directory
1835 the source repository's working directory
1833 c) the changeset specified with -u (if a branch name, this means the
1836 c) the changeset specified with -u (if a branch name, this means the
1834 latest head of that branch)
1837 latest head of that branch)
1835 d) the changeset specified with -r
1838 d) the changeset specified with -r
1836 e) the tipmost head specified with -b
1839 e) the tipmost head specified with -b
1837 f) the tipmost head specified with the url#branch source syntax
1840 f) the tipmost head specified with the url#branch source syntax
1838 g) the revision marked with the '@' bookmark, if present
1841 g) the revision marked with the '@' bookmark, if present
1839 h) the tipmost head of the default branch
1842 h) the tipmost head of the default branch
1840 i) tip
1843 i) tip
1841
1844
1842 When cloning from servers that support it, Mercurial may fetch
1845 When cloning from servers that support it, Mercurial may fetch
1843 pre-generated data from a server-advertised URL or inline from the
1846 pre-generated data from a server-advertised URL or inline from the
1844 same stream. When this is done, hooks operating on incoming changesets
1847 same stream. When this is done, hooks operating on incoming changesets
1845 and changegroups may fire more than once, once for each pre-generated
1848 and changegroups may fire more than once, once for each pre-generated
1846 bundle and as well as for any additional remaining data. In addition,
1849 bundle and as well as for any additional remaining data. In addition,
1847 if an error occurs, the repository may be rolled back to a partial
1850 if an error occurs, the repository may be rolled back to a partial
1848 clone. This behavior may change in future releases.
1851 clone. This behavior may change in future releases.
1849 See :hg:`help -e clonebundles` for more.
1852 See :hg:`help -e clonebundles` for more.
1850
1853
1851 Examples:
1854 Examples:
1852
1855
1853 - clone a remote repository to a new directory named hg/::
1856 - clone a remote repository to a new directory named hg/::
1854
1857
1855 hg clone https://www.mercurial-scm.org/repo/hg/
1858 hg clone https://www.mercurial-scm.org/repo/hg/
1856
1859
1857 - create a lightweight local clone::
1860 - create a lightweight local clone::
1858
1861
1859 hg clone project/ project-feature/
1862 hg clone project/ project-feature/
1860
1863
1861 - clone from an absolute path on an ssh server (note double-slash)::
1864 - clone from an absolute path on an ssh server (note double-slash)::
1862
1865
1863 hg clone ssh://user@server//home/projects/alpha/
1866 hg clone ssh://user@server//home/projects/alpha/
1864
1867
1865 - do a streaming clone while checking out a specified version::
1868 - do a streaming clone while checking out a specified version::
1866
1869
1867 hg clone --stream http://server/repo -u 1.5
1870 hg clone --stream http://server/repo -u 1.5
1868
1871
1869 - create a repository without changesets after a particular revision::
1872 - create a repository without changesets after a particular revision::
1870
1873
1871 hg clone -r 04e544 experimental/ good/
1874 hg clone -r 04e544 experimental/ good/
1872
1875
1873 - clone (and track) a particular named branch::
1876 - clone (and track) a particular named branch::
1874
1877
1875 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1878 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1876
1879
1877 See :hg:`help urls` for details on specifying URLs.
1880 See :hg:`help urls` for details on specifying URLs.
1878
1881
1879 Returns 0 on success.
1882 Returns 0 on success.
1880 """
1883 """
1881 opts = pycompat.byteskwargs(opts)
1884 opts = pycompat.byteskwargs(opts)
1882 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1885 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1883
1886
1884 # --include/--exclude can come from narrow or sparse.
1887 # --include/--exclude can come from narrow or sparse.
1885 includepats, excludepats = None, None
1888 includepats, excludepats = None, None
1886
1889
1887 # hg.clone() differentiates between None and an empty set. So make sure
1890 # hg.clone() differentiates between None and an empty set. So make sure
1888 # patterns are sets if narrow is requested without patterns.
1891 # patterns are sets if narrow is requested without patterns.
1889 if opts.get(b'narrow'):
1892 if opts.get(b'narrow'):
1890 includepats = set()
1893 includepats = set()
1891 excludepats = set()
1894 excludepats = set()
1892
1895
1893 if opts.get(b'include'):
1896 if opts.get(b'include'):
1894 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1897 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1895 if opts.get(b'exclude'):
1898 if opts.get(b'exclude'):
1896 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1899 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1897
1900
1898 r = hg.clone(
1901 r = hg.clone(
1899 ui,
1902 ui,
1900 opts,
1903 opts,
1901 source,
1904 source,
1902 dest,
1905 dest,
1903 pull=opts.get(b'pull'),
1906 pull=opts.get(b'pull'),
1904 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1907 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1905 revs=opts.get(b'rev'),
1908 revs=opts.get(b'rev'),
1906 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1909 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1907 branch=opts.get(b'branch'),
1910 branch=opts.get(b'branch'),
1908 shareopts=opts.get(b'shareopts'),
1911 shareopts=opts.get(b'shareopts'),
1909 storeincludepats=includepats,
1912 storeincludepats=includepats,
1910 storeexcludepats=excludepats,
1913 storeexcludepats=excludepats,
1911 depth=opts.get(b'depth') or None,
1914 depth=opts.get(b'depth') or None,
1912 )
1915 )
1913
1916
1914 return r is None
1917 return r is None
1915
1918
1916
1919
1917 @command(
1920 @command(
1918 b'commit|ci',
1921 b'commit|ci',
1919 [
1922 [
1920 (
1923 (
1921 b'A',
1924 b'A',
1922 b'addremove',
1925 b'addremove',
1923 None,
1926 None,
1924 _(b'mark new/missing files as added/removed before committing'),
1927 _(b'mark new/missing files as added/removed before committing'),
1925 ),
1928 ),
1926 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1929 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1927 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1930 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1928 (b's', b'secret', None, _(b'use the secret phase for committing')),
1931 (b's', b'secret', None, _(b'use the secret phase for committing')),
1929 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1932 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1930 (
1933 (
1931 b'',
1934 b'',
1932 b'force-close-branch',
1935 b'force-close-branch',
1933 None,
1936 None,
1934 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1937 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1935 ),
1938 ),
1936 (b'i', b'interactive', None, _(b'use interactive mode')),
1939 (b'i', b'interactive', None, _(b'use interactive mode')),
1937 ]
1940 ]
1938 + walkopts
1941 + walkopts
1939 + commitopts
1942 + commitopts
1940 + commitopts2
1943 + commitopts2
1941 + subrepoopts,
1944 + subrepoopts,
1942 _(b'[OPTION]... [FILE]...'),
1945 _(b'[OPTION]... [FILE]...'),
1943 helpcategory=command.CATEGORY_COMMITTING,
1946 helpcategory=command.CATEGORY_COMMITTING,
1944 helpbasic=True,
1947 helpbasic=True,
1945 inferrepo=True,
1948 inferrepo=True,
1946 )
1949 )
1947 def commit(ui, repo, *pats, **opts):
1950 def commit(ui, repo, *pats, **opts):
1948 """commit the specified files or all outstanding changes
1951 """commit the specified files or all outstanding changes
1949
1952
1950 Commit changes to the given files into the repository. Unlike a
1953 Commit changes to the given files into the repository. Unlike a
1951 centralized SCM, this operation is a local operation. See
1954 centralized SCM, this operation is a local operation. See
1952 :hg:`push` for a way to actively distribute your changes.
1955 :hg:`push` for a way to actively distribute your changes.
1953
1956
1954 If a list of files is omitted, all changes reported by :hg:`status`
1957 If a list of files is omitted, all changes reported by :hg:`status`
1955 will be committed.
1958 will be committed.
1956
1959
1957 If you are committing the result of a merge, do not provide any
1960 If you are committing the result of a merge, do not provide any
1958 filenames or -I/-X filters.
1961 filenames or -I/-X filters.
1959
1962
1960 If no commit message is specified, Mercurial starts your
1963 If no commit message is specified, Mercurial starts your
1961 configured editor where you can enter a message. In case your
1964 configured editor where you can enter a message. In case your
1962 commit fails, you will find a backup of your message in
1965 commit fails, you will find a backup of your message in
1963 ``.hg/last-message.txt``.
1966 ``.hg/last-message.txt``.
1964
1967
1965 The --close-branch flag can be used to mark the current branch
1968 The --close-branch flag can be used to mark the current branch
1966 head closed. When all heads of a branch are closed, the branch
1969 head closed. When all heads of a branch are closed, the branch
1967 will be considered closed and no longer listed.
1970 will be considered closed and no longer listed.
1968
1971
1969 The --amend flag can be used to amend the parent of the
1972 The --amend flag can be used to amend the parent of the
1970 working directory with a new commit that contains the changes
1973 working directory with a new commit that contains the changes
1971 in the parent in addition to those currently reported by :hg:`status`,
1974 in the parent in addition to those currently reported by :hg:`status`,
1972 if there are any. The old commit is stored in a backup bundle in
1975 if there are any. The old commit is stored in a backup bundle in
1973 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1976 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1974 on how to restore it).
1977 on how to restore it).
1975
1978
1976 Message, user and date are taken from the amended commit unless
1979 Message, user and date are taken from the amended commit unless
1977 specified. When a message isn't specified on the command line,
1980 specified. When a message isn't specified on the command line,
1978 the editor will open with the message of the amended commit.
1981 the editor will open with the message of the amended commit.
1979
1982
1980 It is not possible to amend public changesets (see :hg:`help phases`)
1983 It is not possible to amend public changesets (see :hg:`help phases`)
1981 or changesets that have children.
1984 or changesets that have children.
1982
1985
1983 See :hg:`help dates` for a list of formats valid for -d/--date.
1986 See :hg:`help dates` for a list of formats valid for -d/--date.
1984
1987
1985 Returns 0 on success, 1 if nothing changed.
1988 Returns 0 on success, 1 if nothing changed.
1986
1989
1987 .. container:: verbose
1990 .. container:: verbose
1988
1991
1989 Examples:
1992 Examples:
1990
1993
1991 - commit all files ending in .py::
1994 - commit all files ending in .py::
1992
1995
1993 hg commit --include "set:**.py"
1996 hg commit --include "set:**.py"
1994
1997
1995 - commit all non-binary files::
1998 - commit all non-binary files::
1996
1999
1997 hg commit --exclude "set:binary()"
2000 hg commit --exclude "set:binary()"
1998
2001
1999 - amend the current commit and set the date to now::
2002 - amend the current commit and set the date to now::
2000
2003
2001 hg commit --amend --date now
2004 hg commit --amend --date now
2002 """
2005 """
2003 with repo.wlock(), repo.lock():
2006 with repo.wlock(), repo.lock():
2004 return _docommit(ui, repo, *pats, **opts)
2007 return _docommit(ui, repo, *pats, **opts)
2005
2008
2006
2009
2007 def _docommit(ui, repo, *pats, **opts):
2010 def _docommit(ui, repo, *pats, **opts):
2008 if opts.get('interactive'):
2011 if opts.get('interactive'):
2009 opts.pop('interactive')
2012 opts.pop('interactive')
2010 ret = cmdutil.dorecord(
2013 ret = cmdutil.dorecord(
2011 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2014 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2012 )
2015 )
2013 # ret can be 0 (no changes to record) or the value returned by
2016 # ret can be 0 (no changes to record) or the value returned by
2014 # commit(), 1 if nothing changed or None on success.
2017 # commit(), 1 if nothing changed or None on success.
2015 return 1 if ret == 0 else ret
2018 return 1 if ret == 0 else ret
2016
2019
2017 opts = pycompat.byteskwargs(opts)
2020 opts = pycompat.byteskwargs(opts)
2018 if opts.get(b'subrepos'):
2021 if opts.get(b'subrepos'):
2019 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2022 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2020 # Let --subrepos on the command line override config setting.
2023 # Let --subrepos on the command line override config setting.
2021 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2024 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2022
2025
2023 cmdutil.checkunfinished(repo, commit=True)
2026 cmdutil.checkunfinished(repo, commit=True)
2024
2027
2025 branch = repo[None].branch()
2028 branch = repo[None].branch()
2026 bheads = repo.branchheads(branch)
2029 bheads = repo.branchheads(branch)
2030 tip = repo.changelog.tip()
2027
2031
2028 extra = {}
2032 extra = {}
2029 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2033 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2030 extra[b'close'] = b'1'
2034 extra[b'close'] = b'1'
2031
2035
2032 if repo[b'.'].closesbranch():
2036 if repo[b'.'].closesbranch():
2033 raise error.Abort(
2037 raise error.Abort(
2034 _(b'current revision is already a branch closing head')
2038 _(b'current revision is already a branch closing head')
2035 )
2039 )
2036 elif not bheads:
2040 elif not bheads:
2037 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2041 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2038 elif (
2042 elif (
2039 branch == repo[b'.'].branch()
2043 branch == repo[b'.'].branch()
2040 and repo[b'.'].node() not in bheads
2044 and repo[b'.'].node() not in bheads
2041 and not opts.get(b'force_close_branch')
2045 and not opts.get(b'force_close_branch')
2042 ):
2046 ):
2043 hint = _(
2047 hint = _(
2044 b'use --force-close-branch to close branch from a non-head'
2048 b'use --force-close-branch to close branch from a non-head'
2045 b' changeset'
2049 b' changeset'
2046 )
2050 )
2047 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2051 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2048 elif opts.get(b'amend'):
2052 elif opts.get(b'amend'):
2049 if (
2053 if (
2050 repo[b'.'].p1().branch() != branch
2054 repo[b'.'].p1().branch() != branch
2051 and repo[b'.'].p2().branch() != branch
2055 and repo[b'.'].p2().branch() != branch
2052 ):
2056 ):
2053 raise error.Abort(_(b'can only close branch heads'))
2057 raise error.Abort(_(b'can only close branch heads'))
2054
2058
2055 if opts.get(b'amend'):
2059 if opts.get(b'amend'):
2056 if ui.configbool(b'ui', b'commitsubrepos'):
2060 if ui.configbool(b'ui', b'commitsubrepos'):
2057 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2061 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2058
2062
2059 old = repo[b'.']
2063 old = repo[b'.']
2060 rewriteutil.precheck(repo, [old.rev()], b'amend')
2064 rewriteutil.precheck(repo, [old.rev()], b'amend')
2061
2065
2062 # Currently histedit gets confused if an amend happens while histedit
2066 # Currently histedit gets confused if an amend happens while histedit
2063 # is in progress. Since we have a checkunfinished command, we are
2067 # is in progress. Since we have a checkunfinished command, we are
2064 # temporarily honoring it.
2068 # temporarily honoring it.
2065 #
2069 #
2066 # Note: eventually this guard will be removed. Please do not expect
2070 # Note: eventually this guard will be removed. Please do not expect
2067 # this behavior to remain.
2071 # this behavior to remain.
2068 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2072 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2069 cmdutil.checkunfinished(repo)
2073 cmdutil.checkunfinished(repo)
2070
2074
2071 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2075 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2072 if node == old.node():
2076 if node == old.node():
2073 ui.status(_(b"nothing changed\n"))
2077 ui.status(_(b"nothing changed\n"))
2074 return 1
2078 return 1
2075 else:
2079 else:
2076
2080
2077 def commitfunc(ui, repo, message, match, opts):
2081 def commitfunc(ui, repo, message, match, opts):
2078 overrides = {}
2082 overrides = {}
2079 if opts.get(b'secret'):
2083 if opts.get(b'secret'):
2080 overrides[(b'phases', b'new-commit')] = b'secret'
2084 overrides[(b'phases', b'new-commit')] = b'secret'
2081
2085
2082 baseui = repo.baseui
2086 baseui = repo.baseui
2083 with baseui.configoverride(overrides, b'commit'):
2087 with baseui.configoverride(overrides, b'commit'):
2084 with ui.configoverride(overrides, b'commit'):
2088 with ui.configoverride(overrides, b'commit'):
2085 editform = cmdutil.mergeeditform(
2089 editform = cmdutil.mergeeditform(
2086 repo[None], b'commit.normal'
2090 repo[None], b'commit.normal'
2087 )
2091 )
2088 editor = cmdutil.getcommiteditor(
2092 editor = cmdutil.getcommiteditor(
2089 editform=editform, **pycompat.strkwargs(opts)
2093 editform=editform, **pycompat.strkwargs(opts)
2090 )
2094 )
2091 return repo.commit(
2095 return repo.commit(
2092 message,
2096 message,
2093 opts.get(b'user'),
2097 opts.get(b'user'),
2094 opts.get(b'date'),
2098 opts.get(b'date'),
2095 match,
2099 match,
2096 editor=editor,
2100 editor=editor,
2097 extra=extra,
2101 extra=extra,
2098 )
2102 )
2099
2103
2100 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2104 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2101
2105
2102 if not node:
2106 if not node:
2103 stat = cmdutil.postcommitstatus(repo, pats, opts)
2107 stat = cmdutil.postcommitstatus(repo, pats, opts)
2104 if stat.deleted:
2108 if stat.deleted:
2105 ui.status(
2109 ui.status(
2106 _(
2110 _(
2107 b"nothing changed (%d missing files, see "
2111 b"nothing changed (%d missing files, see "
2108 b"'hg status')\n"
2112 b"'hg status')\n"
2109 )
2113 )
2110 % len(stat.deleted)
2114 % len(stat.deleted)
2111 )
2115 )
2112 else:
2116 else:
2113 ui.status(_(b"nothing changed\n"))
2117 ui.status(_(b"nothing changed\n"))
2114 return 1
2118 return 1
2115
2119
2116 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2120 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2117
2121
2118 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2122 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2119 status(
2123 status(
2120 ui,
2124 ui,
2121 repo,
2125 repo,
2122 modified=True,
2126 modified=True,
2123 added=True,
2127 added=True,
2124 removed=True,
2128 removed=True,
2125 deleted=True,
2129 deleted=True,
2126 unknown=True,
2130 unknown=True,
2127 subrepos=opts.get(b'subrepos'),
2131 subrepos=opts.get(b'subrepos'),
2128 )
2132 )
2129
2133
2130
2134
2131 @command(
2135 @command(
2132 b'config|showconfig|debugconfig',
2136 b'config|showconfig|debugconfig',
2133 [
2137 [
2134 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2138 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2135 (b'e', b'edit', None, _(b'edit user config')),
2139 (b'e', b'edit', None, _(b'edit user config')),
2136 (b'l', b'local', None, _(b'edit repository config')),
2140 (b'l', b'local', None, _(b'edit repository config')),
2137 (
2141 (
2138 b'',
2142 b'',
2139 b'shared',
2143 b'shared',
2140 None,
2144 None,
2141 _(b'edit shared source repository config (EXPERIMENTAL)'),
2145 _(b'edit shared source repository config (EXPERIMENTAL)'),
2142 ),
2146 ),
2143 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2147 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2144 (b'g', b'global', None, _(b'edit global config')),
2148 (b'g', b'global', None, _(b'edit global config')),
2145 ]
2149 ]
2146 + formatteropts,
2150 + formatteropts,
2147 _(b'[-u] [NAME]...'),
2151 _(b'[-u] [NAME]...'),
2148 helpcategory=command.CATEGORY_HELP,
2152 helpcategory=command.CATEGORY_HELP,
2149 optionalrepo=True,
2153 optionalrepo=True,
2150 intents={INTENT_READONLY},
2154 intents={INTENT_READONLY},
2151 )
2155 )
2152 def config(ui, repo, *values, **opts):
2156 def config(ui, repo, *values, **opts):
2153 """show combined config settings from all hgrc files
2157 """show combined config settings from all hgrc files
2154
2158
2155 With no arguments, print names and values of all config items.
2159 With no arguments, print names and values of all config items.
2156
2160
2157 With one argument of the form section.name, print just the value
2161 With one argument of the form section.name, print just the value
2158 of that config item.
2162 of that config item.
2159
2163
2160 With multiple arguments, print names and values of all config
2164 With multiple arguments, print names and values of all config
2161 items with matching section names or section.names.
2165 items with matching section names or section.names.
2162
2166
2163 With --edit, start an editor on the user-level config file. With
2167 With --edit, start an editor on the user-level config file. With
2164 --global, edit the system-wide config file. With --local, edit the
2168 --global, edit the system-wide config file. With --local, edit the
2165 repository-level config file.
2169 repository-level config file.
2166
2170
2167 With --debug, the source (filename and line number) is printed
2171 With --debug, the source (filename and line number) is printed
2168 for each config item.
2172 for each config item.
2169
2173
2170 See :hg:`help config` for more information about config files.
2174 See :hg:`help config` for more information about config files.
2171
2175
2172 .. container:: verbose
2176 .. container:: verbose
2173
2177
2174 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2178 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2175 This file is not shared across shares when in share-safe mode.
2179 This file is not shared across shares when in share-safe mode.
2176
2180
2177 Template:
2181 Template:
2178
2182
2179 The following keywords are supported. See also :hg:`help templates`.
2183 The following keywords are supported. See also :hg:`help templates`.
2180
2184
2181 :name: String. Config name.
2185 :name: String. Config name.
2182 :source: String. Filename and line number where the item is defined.
2186 :source: String. Filename and line number where the item is defined.
2183 :value: String. Config value.
2187 :value: String. Config value.
2184
2188
2185 The --shared flag can be used to edit the config file of shared source
2189 The --shared flag can be used to edit the config file of shared source
2186 repository. It only works when you have shared using the experimental
2190 repository. It only works when you have shared using the experimental
2187 share safe feature.
2191 share safe feature.
2188
2192
2189 Returns 0 on success, 1 if NAME does not exist.
2193 Returns 0 on success, 1 if NAME does not exist.
2190
2194
2191 """
2195 """
2192
2196
2193 opts = pycompat.byteskwargs(opts)
2197 opts = pycompat.byteskwargs(opts)
2194 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2198 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2195 if any(opts.get(o) for o in editopts):
2199 if any(opts.get(o) for o in editopts):
2196 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2200 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2197 if opts.get(b'local'):
2201 if opts.get(b'local'):
2198 if not repo:
2202 if not repo:
2199 raise error.Abort(_(b"can't use --local outside a repository"))
2203 raise error.Abort(_(b"can't use --local outside a repository"))
2200 paths = [repo.vfs.join(b'hgrc')]
2204 paths = [repo.vfs.join(b'hgrc')]
2201 elif opts.get(b'global'):
2205 elif opts.get(b'global'):
2202 paths = rcutil.systemrcpath()
2206 paths = rcutil.systemrcpath()
2203 elif opts.get(b'shared'):
2207 elif opts.get(b'shared'):
2204 if not repo.shared():
2208 if not repo.shared():
2205 raise error.Abort(
2209 raise error.Abort(
2206 _(b"repository is not shared; can't use --shared")
2210 _(b"repository is not shared; can't use --shared")
2207 )
2211 )
2208 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2212 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2209 raise error.Abort(
2213 raise error.Abort(
2210 _(
2214 _(
2211 b"share safe feature not unabled; "
2215 b"share safe feature not unabled; "
2212 b"unable to edit shared source repository config"
2216 b"unable to edit shared source repository config"
2213 )
2217 )
2214 )
2218 )
2215 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2219 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2216 elif opts.get(b'non_shared'):
2220 elif opts.get(b'non_shared'):
2217 paths = [repo.vfs.join(b'hgrc-not-shared')]
2221 paths = [repo.vfs.join(b'hgrc-not-shared')]
2218 else:
2222 else:
2219 paths = rcutil.userrcpath()
2223 paths = rcutil.userrcpath()
2220
2224
2221 for f in paths:
2225 for f in paths:
2222 if os.path.exists(f):
2226 if os.path.exists(f):
2223 break
2227 break
2224 else:
2228 else:
2225 if opts.get(b'global'):
2229 if opts.get(b'global'):
2226 samplehgrc = uimod.samplehgrcs[b'global']
2230 samplehgrc = uimod.samplehgrcs[b'global']
2227 elif opts.get(b'local'):
2231 elif opts.get(b'local'):
2228 samplehgrc = uimod.samplehgrcs[b'local']
2232 samplehgrc = uimod.samplehgrcs[b'local']
2229 else:
2233 else:
2230 samplehgrc = uimod.samplehgrcs[b'user']
2234 samplehgrc = uimod.samplehgrcs[b'user']
2231
2235
2232 f = paths[0]
2236 f = paths[0]
2233 fp = open(f, b"wb")
2237 fp = open(f, b"wb")
2234 fp.write(util.tonativeeol(samplehgrc))
2238 fp.write(util.tonativeeol(samplehgrc))
2235 fp.close()
2239 fp.close()
2236
2240
2237 editor = ui.geteditor()
2241 editor = ui.geteditor()
2238 ui.system(
2242 ui.system(
2239 b"%s \"%s\"" % (editor, f),
2243 b"%s \"%s\"" % (editor, f),
2240 onerr=error.Abort,
2244 onerr=error.Abort,
2241 errprefix=_(b"edit failed"),
2245 errprefix=_(b"edit failed"),
2242 blockedtag=b'config_edit',
2246 blockedtag=b'config_edit',
2243 )
2247 )
2244 return
2248 return
2245 ui.pager(b'config')
2249 ui.pager(b'config')
2246 fm = ui.formatter(b'config', opts)
2250 fm = ui.formatter(b'config', opts)
2247 for t, f in rcutil.rccomponents():
2251 for t, f in rcutil.rccomponents():
2248 if t == b'path':
2252 if t == b'path':
2249 ui.debug(b'read config from: %s\n' % f)
2253 ui.debug(b'read config from: %s\n' % f)
2250 elif t == b'resource':
2254 elif t == b'resource':
2251 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2255 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2252 elif t == b'items':
2256 elif t == b'items':
2253 # Don't print anything for 'items'.
2257 # Don't print anything for 'items'.
2254 pass
2258 pass
2255 else:
2259 else:
2256 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2260 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2257 untrusted = bool(opts.get(b'untrusted'))
2261 untrusted = bool(opts.get(b'untrusted'))
2258
2262
2259 selsections = selentries = []
2263 selsections = selentries = []
2260 if values:
2264 if values:
2261 selsections = [v for v in values if b'.' not in v]
2265 selsections = [v for v in values if b'.' not in v]
2262 selentries = [v for v in values if b'.' in v]
2266 selentries = [v for v in values if b'.' in v]
2263 uniquesel = len(selentries) == 1 and not selsections
2267 uniquesel = len(selentries) == 1 and not selsections
2264 selsections = set(selsections)
2268 selsections = set(selsections)
2265 selentries = set(selentries)
2269 selentries = set(selentries)
2266
2270
2267 matched = False
2271 matched = False
2268 for section, name, value in ui.walkconfig(untrusted=untrusted):
2272 for section, name, value in ui.walkconfig(untrusted=untrusted):
2269 source = ui.configsource(section, name, untrusted)
2273 source = ui.configsource(section, name, untrusted)
2270 value = pycompat.bytestr(value)
2274 value = pycompat.bytestr(value)
2271 defaultvalue = ui.configdefault(section, name)
2275 defaultvalue = ui.configdefault(section, name)
2272 if fm.isplain():
2276 if fm.isplain():
2273 source = source or b'none'
2277 source = source or b'none'
2274 value = value.replace(b'\n', b'\\n')
2278 value = value.replace(b'\n', b'\\n')
2275 entryname = section + b'.' + name
2279 entryname = section + b'.' + name
2276 if values and not (section in selsections or entryname in selentries):
2280 if values and not (section in selsections or entryname in selentries):
2277 continue
2281 continue
2278 fm.startitem()
2282 fm.startitem()
2279 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2283 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2280 if uniquesel:
2284 if uniquesel:
2281 fm.data(name=entryname)
2285 fm.data(name=entryname)
2282 fm.write(b'value', b'%s\n', value)
2286 fm.write(b'value', b'%s\n', value)
2283 else:
2287 else:
2284 fm.write(b'name value', b'%s=%s\n', entryname, value)
2288 fm.write(b'name value', b'%s=%s\n', entryname, value)
2285 if formatter.isprintable(defaultvalue):
2289 if formatter.isprintable(defaultvalue):
2286 fm.data(defaultvalue=defaultvalue)
2290 fm.data(defaultvalue=defaultvalue)
2287 elif isinstance(defaultvalue, list) and all(
2291 elif isinstance(defaultvalue, list) and all(
2288 formatter.isprintable(e) for e in defaultvalue
2292 formatter.isprintable(e) for e in defaultvalue
2289 ):
2293 ):
2290 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2294 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2291 # TODO: no idea how to process unsupported defaultvalue types
2295 # TODO: no idea how to process unsupported defaultvalue types
2292 matched = True
2296 matched = True
2293 fm.end()
2297 fm.end()
2294 if matched:
2298 if matched:
2295 return 0
2299 return 0
2296 return 1
2300 return 1
2297
2301
2298
2302
2299 @command(
2303 @command(
2300 b'continue',
2304 b'continue',
2301 dryrunopts,
2305 dryrunopts,
2302 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2306 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2303 helpbasic=True,
2307 helpbasic=True,
2304 )
2308 )
2305 def continuecmd(ui, repo, **opts):
2309 def continuecmd(ui, repo, **opts):
2306 """resumes an interrupted operation (EXPERIMENTAL)
2310 """resumes an interrupted operation (EXPERIMENTAL)
2307
2311
2308 Finishes a multistep operation like graft, histedit, rebase, merge,
2312 Finishes a multistep operation like graft, histedit, rebase, merge,
2309 and unshelve if they are in an interrupted state.
2313 and unshelve if they are in an interrupted state.
2310
2314
2311 use --dry-run/-n to dry run the command.
2315 use --dry-run/-n to dry run the command.
2312 """
2316 """
2313 dryrun = opts.get('dry_run')
2317 dryrun = opts.get('dry_run')
2314 contstate = cmdutil.getunfinishedstate(repo)
2318 contstate = cmdutil.getunfinishedstate(repo)
2315 if not contstate:
2319 if not contstate:
2316 raise error.Abort(_(b'no operation in progress'))
2320 raise error.Abort(_(b'no operation in progress'))
2317 if not contstate.continuefunc:
2321 if not contstate.continuefunc:
2318 raise error.Abort(
2322 raise error.Abort(
2319 (
2323 (
2320 _(b"%s in progress but does not support 'hg continue'")
2324 _(b"%s in progress but does not support 'hg continue'")
2321 % (contstate._opname)
2325 % (contstate._opname)
2322 ),
2326 ),
2323 hint=contstate.continuemsg(),
2327 hint=contstate.continuemsg(),
2324 )
2328 )
2325 if dryrun:
2329 if dryrun:
2326 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2330 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2327 return
2331 return
2328 return contstate.continuefunc(ui, repo)
2332 return contstate.continuefunc(ui, repo)
2329
2333
2330
2334
2331 @command(
2335 @command(
2332 b'copy|cp',
2336 b'copy|cp',
2333 [
2337 [
2334 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2338 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2335 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2339 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2336 (
2340 (
2337 b'',
2341 b'',
2338 b'at-rev',
2342 b'at-rev',
2339 b'',
2343 b'',
2340 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2344 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2341 _(b'REV'),
2345 _(b'REV'),
2342 ),
2346 ),
2343 (
2347 (
2344 b'f',
2348 b'f',
2345 b'force',
2349 b'force',
2346 None,
2350 None,
2347 _(b'forcibly copy over an existing managed file'),
2351 _(b'forcibly copy over an existing managed file'),
2348 ),
2352 ),
2349 ]
2353 ]
2350 + walkopts
2354 + walkopts
2351 + dryrunopts,
2355 + dryrunopts,
2352 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2356 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2353 helpcategory=command.CATEGORY_FILE_CONTENTS,
2357 helpcategory=command.CATEGORY_FILE_CONTENTS,
2354 )
2358 )
2355 def copy(ui, repo, *pats, **opts):
2359 def copy(ui, repo, *pats, **opts):
2356 """mark files as copied for the next commit
2360 """mark files as copied for the next commit
2357
2361
2358 Mark dest as having copies of source files. If dest is a
2362 Mark dest as having copies of source files. If dest is a
2359 directory, copies are put in that directory. If dest is a file,
2363 directory, copies are put in that directory. If dest is a file,
2360 the source must be a single file.
2364 the source must be a single file.
2361
2365
2362 By default, this command copies the contents of files as they
2366 By default, this command copies the contents of files as they
2363 exist in the working directory. If invoked with -A/--after, the
2367 exist in the working directory. If invoked with -A/--after, the
2364 operation is recorded, but no copying is performed.
2368 operation is recorded, but no copying is performed.
2365
2369
2366 To undo marking a destination file as copied, use --forget. With that
2370 To undo marking a destination file as copied, use --forget. With that
2367 option, all given (positional) arguments are unmarked as copies. The
2371 option, all given (positional) arguments are unmarked as copies. The
2368 destination file(s) will be left in place (still tracked).
2372 destination file(s) will be left in place (still tracked).
2369
2373
2370 This command takes effect with the next commit by default.
2374 This command takes effect with the next commit by default.
2371
2375
2372 Returns 0 on success, 1 if errors are encountered.
2376 Returns 0 on success, 1 if errors are encountered.
2373 """
2377 """
2374 opts = pycompat.byteskwargs(opts)
2378 opts = pycompat.byteskwargs(opts)
2375 with repo.wlock():
2379 with repo.wlock():
2376 return cmdutil.copy(ui, repo, pats, opts)
2380 return cmdutil.copy(ui, repo, pats, opts)
2377
2381
2378
2382
2379 @command(
2383 @command(
2380 b'debugcommands',
2384 b'debugcommands',
2381 [],
2385 [],
2382 _(b'[COMMAND]'),
2386 _(b'[COMMAND]'),
2383 helpcategory=command.CATEGORY_HELP,
2387 helpcategory=command.CATEGORY_HELP,
2384 norepo=True,
2388 norepo=True,
2385 )
2389 )
2386 def debugcommands(ui, cmd=b'', *args):
2390 def debugcommands(ui, cmd=b'', *args):
2387 """list all available commands and options"""
2391 """list all available commands and options"""
2388 for cmd, vals in sorted(pycompat.iteritems(table)):
2392 for cmd, vals in sorted(pycompat.iteritems(table)):
2389 cmd = cmd.split(b'|')[0]
2393 cmd = cmd.split(b'|')[0]
2390 opts = b', '.join([i[1] for i in vals[1]])
2394 opts = b', '.join([i[1] for i in vals[1]])
2391 ui.write(b'%s: %s\n' % (cmd, opts))
2395 ui.write(b'%s: %s\n' % (cmd, opts))
2392
2396
2393
2397
2394 @command(
2398 @command(
2395 b'debugcomplete',
2399 b'debugcomplete',
2396 [(b'o', b'options', None, _(b'show the command options'))],
2400 [(b'o', b'options', None, _(b'show the command options'))],
2397 _(b'[-o] CMD'),
2401 _(b'[-o] CMD'),
2398 helpcategory=command.CATEGORY_HELP,
2402 helpcategory=command.CATEGORY_HELP,
2399 norepo=True,
2403 norepo=True,
2400 )
2404 )
2401 def debugcomplete(ui, cmd=b'', **opts):
2405 def debugcomplete(ui, cmd=b'', **opts):
2402 """returns the completion list associated with the given command"""
2406 """returns the completion list associated with the given command"""
2403
2407
2404 if opts.get('options'):
2408 if opts.get('options'):
2405 options = []
2409 options = []
2406 otables = [globalopts]
2410 otables = [globalopts]
2407 if cmd:
2411 if cmd:
2408 aliases, entry = cmdutil.findcmd(cmd, table, False)
2412 aliases, entry = cmdutil.findcmd(cmd, table, False)
2409 otables.append(entry[1])
2413 otables.append(entry[1])
2410 for t in otables:
2414 for t in otables:
2411 for o in t:
2415 for o in t:
2412 if b"(DEPRECATED)" in o[3]:
2416 if b"(DEPRECATED)" in o[3]:
2413 continue
2417 continue
2414 if o[0]:
2418 if o[0]:
2415 options.append(b'-%s' % o[0])
2419 options.append(b'-%s' % o[0])
2416 options.append(b'--%s' % o[1])
2420 options.append(b'--%s' % o[1])
2417 ui.write(b"%s\n" % b"\n".join(options))
2421 ui.write(b"%s\n" % b"\n".join(options))
2418 return
2422 return
2419
2423
2420 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2424 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2421 if ui.verbose:
2425 if ui.verbose:
2422 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2426 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2423 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2427 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2424
2428
2425
2429
2426 @command(
2430 @command(
2427 b'diff',
2431 b'diff',
2428 [
2432 [
2429 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2433 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2430 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2434 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2431 ]
2435 ]
2432 + diffopts
2436 + diffopts
2433 + diffopts2
2437 + diffopts2
2434 + walkopts
2438 + walkopts
2435 + subrepoopts,
2439 + subrepoopts,
2436 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2440 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2437 helpcategory=command.CATEGORY_FILE_CONTENTS,
2441 helpcategory=command.CATEGORY_FILE_CONTENTS,
2438 helpbasic=True,
2442 helpbasic=True,
2439 inferrepo=True,
2443 inferrepo=True,
2440 intents={INTENT_READONLY},
2444 intents={INTENT_READONLY},
2441 )
2445 )
2442 def diff(ui, repo, *pats, **opts):
2446 def diff(ui, repo, *pats, **opts):
2443 """diff repository (or selected files)
2447 """diff repository (or selected files)
2444
2448
2445 Show differences between revisions for the specified files.
2449 Show differences between revisions for the specified files.
2446
2450
2447 Differences between files are shown using the unified diff format.
2451 Differences between files are shown using the unified diff format.
2448
2452
2449 .. note::
2453 .. note::
2450
2454
2451 :hg:`diff` may generate unexpected results for merges, as it will
2455 :hg:`diff` may generate unexpected results for merges, as it will
2452 default to comparing against the working directory's first
2456 default to comparing against the working directory's first
2453 parent changeset if no revisions are specified.
2457 parent changeset if no revisions are specified.
2454
2458
2455 When two revision arguments are given, then changes are shown
2459 When two revision arguments are given, then changes are shown
2456 between those revisions. If only one revision is specified then
2460 between those revisions. If only one revision is specified then
2457 that revision is compared to the working directory, and, when no
2461 that revision is compared to the working directory, and, when no
2458 revisions are specified, the working directory files are compared
2462 revisions are specified, the working directory files are compared
2459 to its first parent.
2463 to its first parent.
2460
2464
2461 Alternatively you can specify -c/--change with a revision to see
2465 Alternatively you can specify -c/--change with a revision to see
2462 the changes in that changeset relative to its first parent.
2466 the changes in that changeset relative to its first parent.
2463
2467
2464 Without the -a/--text option, diff will avoid generating diffs of
2468 Without the -a/--text option, diff will avoid generating diffs of
2465 files it detects as binary. With -a, diff will generate a diff
2469 files it detects as binary. With -a, diff will generate a diff
2466 anyway, probably with undesirable results.
2470 anyway, probably with undesirable results.
2467
2471
2468 Use the -g/--git option to generate diffs in the git extended diff
2472 Use the -g/--git option to generate diffs in the git extended diff
2469 format. For more information, read :hg:`help diffs`.
2473 format. For more information, read :hg:`help diffs`.
2470
2474
2471 .. container:: verbose
2475 .. container:: verbose
2472
2476
2473 Examples:
2477 Examples:
2474
2478
2475 - compare a file in the current working directory to its parent::
2479 - compare a file in the current working directory to its parent::
2476
2480
2477 hg diff foo.c
2481 hg diff foo.c
2478
2482
2479 - compare two historical versions of a directory, with rename info::
2483 - compare two historical versions of a directory, with rename info::
2480
2484
2481 hg diff --git -r 1.0:1.2 lib/
2485 hg diff --git -r 1.0:1.2 lib/
2482
2486
2483 - get change stats relative to the last change on some date::
2487 - get change stats relative to the last change on some date::
2484
2488
2485 hg diff --stat -r "date('may 2')"
2489 hg diff --stat -r "date('may 2')"
2486
2490
2487 - diff all newly-added files that contain a keyword::
2491 - diff all newly-added files that contain a keyword::
2488
2492
2489 hg diff "set:added() and grep(GNU)"
2493 hg diff "set:added() and grep(GNU)"
2490
2494
2491 - compare a revision and its parents::
2495 - compare a revision and its parents::
2492
2496
2493 hg diff -c 9353 # compare against first parent
2497 hg diff -c 9353 # compare against first parent
2494 hg diff -r 9353^:9353 # same using revset syntax
2498 hg diff -r 9353^:9353 # same using revset syntax
2495 hg diff -r 9353^2:9353 # compare against the second parent
2499 hg diff -r 9353^2:9353 # compare against the second parent
2496
2500
2497 Returns 0 on success.
2501 Returns 0 on success.
2498 """
2502 """
2499
2503
2500 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2504 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2501 opts = pycompat.byteskwargs(opts)
2505 opts = pycompat.byteskwargs(opts)
2502 revs = opts.get(b'rev')
2506 revs = opts.get(b'rev')
2503 change = opts.get(b'change')
2507 change = opts.get(b'change')
2504 stat = opts.get(b'stat')
2508 stat = opts.get(b'stat')
2505 reverse = opts.get(b'reverse')
2509 reverse = opts.get(b'reverse')
2506
2510
2507 if change:
2511 if change:
2508 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2512 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2509 ctx2 = scmutil.revsingle(repo, change, None)
2513 ctx2 = scmutil.revsingle(repo, change, None)
2510 ctx1 = ctx2.p1()
2514 ctx1 = ctx2.p1()
2511 else:
2515 else:
2512 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2516 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2513 ctx1, ctx2 = scmutil.revpair(repo, revs)
2517 ctx1, ctx2 = scmutil.revpair(repo, revs)
2514
2518
2515 if reverse:
2519 if reverse:
2516 ctxleft = ctx2
2520 ctxleft = ctx2
2517 ctxright = ctx1
2521 ctxright = ctx1
2518 else:
2522 else:
2519 ctxleft = ctx1
2523 ctxleft = ctx1
2520 ctxright = ctx2
2524 ctxright = ctx2
2521
2525
2522 diffopts = patch.diffallopts(ui, opts)
2526 diffopts = patch.diffallopts(ui, opts)
2523 m = scmutil.match(ctx2, pats, opts)
2527 m = scmutil.match(ctx2, pats, opts)
2524 m = repo.narrowmatch(m)
2528 m = repo.narrowmatch(m)
2525 ui.pager(b'diff')
2529 ui.pager(b'diff')
2526 logcmdutil.diffordiffstat(
2530 logcmdutil.diffordiffstat(
2527 ui,
2531 ui,
2528 repo,
2532 repo,
2529 diffopts,
2533 diffopts,
2530 ctxleft,
2534 ctxleft,
2531 ctxright,
2535 ctxright,
2532 m,
2536 m,
2533 stat=stat,
2537 stat=stat,
2534 listsubrepos=opts.get(b'subrepos'),
2538 listsubrepos=opts.get(b'subrepos'),
2535 root=opts.get(b'root'),
2539 root=opts.get(b'root'),
2536 )
2540 )
2537
2541
2538
2542
2539 @command(
2543 @command(
2540 b'export',
2544 b'export',
2541 [
2545 [
2542 (
2546 (
2543 b'B',
2547 b'B',
2544 b'bookmark',
2548 b'bookmark',
2545 b'',
2549 b'',
2546 _(b'export changes only reachable by given bookmark'),
2550 _(b'export changes only reachable by given bookmark'),
2547 _(b'BOOKMARK'),
2551 _(b'BOOKMARK'),
2548 ),
2552 ),
2549 (
2553 (
2550 b'o',
2554 b'o',
2551 b'output',
2555 b'output',
2552 b'',
2556 b'',
2553 _(b'print output to file with formatted name'),
2557 _(b'print output to file with formatted name'),
2554 _(b'FORMAT'),
2558 _(b'FORMAT'),
2555 ),
2559 ),
2556 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2560 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2557 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2561 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2558 ]
2562 ]
2559 + diffopts
2563 + diffopts
2560 + formatteropts,
2564 + formatteropts,
2561 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2565 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2562 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2566 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2563 helpbasic=True,
2567 helpbasic=True,
2564 intents={INTENT_READONLY},
2568 intents={INTENT_READONLY},
2565 )
2569 )
2566 def export(ui, repo, *changesets, **opts):
2570 def export(ui, repo, *changesets, **opts):
2567 """dump the header and diffs for one or more changesets
2571 """dump the header and diffs for one or more changesets
2568
2572
2569 Print the changeset header and diffs for one or more revisions.
2573 Print the changeset header and diffs for one or more revisions.
2570 If no revision is given, the parent of the working directory is used.
2574 If no revision is given, the parent of the working directory is used.
2571
2575
2572 The information shown in the changeset header is: author, date,
2576 The information shown in the changeset header is: author, date,
2573 branch name (if non-default), changeset hash, parent(s) and commit
2577 branch name (if non-default), changeset hash, parent(s) and commit
2574 comment.
2578 comment.
2575
2579
2576 .. note::
2580 .. note::
2577
2581
2578 :hg:`export` may generate unexpected diff output for merge
2582 :hg:`export` may generate unexpected diff output for merge
2579 changesets, as it will compare the merge changeset against its
2583 changesets, as it will compare the merge changeset against its
2580 first parent only.
2584 first parent only.
2581
2585
2582 Output may be to a file, in which case the name of the file is
2586 Output may be to a file, in which case the name of the file is
2583 given using a template string. See :hg:`help templates`. In addition
2587 given using a template string. See :hg:`help templates`. In addition
2584 to the common template keywords, the following formatting rules are
2588 to the common template keywords, the following formatting rules are
2585 supported:
2589 supported:
2586
2590
2587 :``%%``: literal "%" character
2591 :``%%``: literal "%" character
2588 :``%H``: changeset hash (40 hexadecimal digits)
2592 :``%H``: changeset hash (40 hexadecimal digits)
2589 :``%N``: number of patches being generated
2593 :``%N``: number of patches being generated
2590 :``%R``: changeset revision number
2594 :``%R``: changeset revision number
2591 :``%b``: basename of the exporting repository
2595 :``%b``: basename of the exporting repository
2592 :``%h``: short-form changeset hash (12 hexadecimal digits)
2596 :``%h``: short-form changeset hash (12 hexadecimal digits)
2593 :``%m``: first line of the commit message (only alphanumeric characters)
2597 :``%m``: first line of the commit message (only alphanumeric characters)
2594 :``%n``: zero-padded sequence number, starting at 1
2598 :``%n``: zero-padded sequence number, starting at 1
2595 :``%r``: zero-padded changeset revision number
2599 :``%r``: zero-padded changeset revision number
2596 :``\\``: literal "\\" character
2600 :``\\``: literal "\\" character
2597
2601
2598 Without the -a/--text option, export will avoid generating diffs
2602 Without the -a/--text option, export will avoid generating diffs
2599 of files it detects as binary. With -a, export will generate a
2603 of files it detects as binary. With -a, export will generate a
2600 diff anyway, probably with undesirable results.
2604 diff anyway, probably with undesirable results.
2601
2605
2602 With -B/--bookmark changesets reachable by the given bookmark are
2606 With -B/--bookmark changesets reachable by the given bookmark are
2603 selected.
2607 selected.
2604
2608
2605 Use the -g/--git option to generate diffs in the git extended diff
2609 Use the -g/--git option to generate diffs in the git extended diff
2606 format. See :hg:`help diffs` for more information.
2610 format. See :hg:`help diffs` for more information.
2607
2611
2608 With the --switch-parent option, the diff will be against the
2612 With the --switch-parent option, the diff will be against the
2609 second parent. It can be useful to review a merge.
2613 second parent. It can be useful to review a merge.
2610
2614
2611 .. container:: verbose
2615 .. container:: verbose
2612
2616
2613 Template:
2617 Template:
2614
2618
2615 The following keywords are supported in addition to the common template
2619 The following keywords are supported in addition to the common template
2616 keywords and functions. See also :hg:`help templates`.
2620 keywords and functions. See also :hg:`help templates`.
2617
2621
2618 :diff: String. Diff content.
2622 :diff: String. Diff content.
2619 :parents: List of strings. Parent nodes of the changeset.
2623 :parents: List of strings. Parent nodes of the changeset.
2620
2624
2621 Examples:
2625 Examples:
2622
2626
2623 - use export and import to transplant a bugfix to the current
2627 - use export and import to transplant a bugfix to the current
2624 branch::
2628 branch::
2625
2629
2626 hg export -r 9353 | hg import -
2630 hg export -r 9353 | hg import -
2627
2631
2628 - export all the changesets between two revisions to a file with
2632 - export all the changesets between two revisions to a file with
2629 rename information::
2633 rename information::
2630
2634
2631 hg export --git -r 123:150 > changes.txt
2635 hg export --git -r 123:150 > changes.txt
2632
2636
2633 - split outgoing changes into a series of patches with
2637 - split outgoing changes into a series of patches with
2634 descriptive names::
2638 descriptive names::
2635
2639
2636 hg export -r "outgoing()" -o "%n-%m.patch"
2640 hg export -r "outgoing()" -o "%n-%m.patch"
2637
2641
2638 Returns 0 on success.
2642 Returns 0 on success.
2639 """
2643 """
2640 opts = pycompat.byteskwargs(opts)
2644 opts = pycompat.byteskwargs(opts)
2641 bookmark = opts.get(b'bookmark')
2645 bookmark = opts.get(b'bookmark')
2642 changesets += tuple(opts.get(b'rev', []))
2646 changesets += tuple(opts.get(b'rev', []))
2643
2647
2644 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2648 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2645
2649
2646 if bookmark:
2650 if bookmark:
2647 if bookmark not in repo._bookmarks:
2651 if bookmark not in repo._bookmarks:
2648 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2652 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2649
2653
2650 revs = scmutil.bookmarkrevs(repo, bookmark)
2654 revs = scmutil.bookmarkrevs(repo, bookmark)
2651 else:
2655 else:
2652 if not changesets:
2656 if not changesets:
2653 changesets = [b'.']
2657 changesets = [b'.']
2654
2658
2655 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2659 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2656 revs = scmutil.revrange(repo, changesets)
2660 revs = scmutil.revrange(repo, changesets)
2657
2661
2658 if not revs:
2662 if not revs:
2659 raise error.Abort(_(b"export requires at least one changeset"))
2663 raise error.Abort(_(b"export requires at least one changeset"))
2660 if len(revs) > 1:
2664 if len(revs) > 1:
2661 ui.note(_(b'exporting patches:\n'))
2665 ui.note(_(b'exporting patches:\n'))
2662 else:
2666 else:
2663 ui.note(_(b'exporting patch:\n'))
2667 ui.note(_(b'exporting patch:\n'))
2664
2668
2665 fntemplate = opts.get(b'output')
2669 fntemplate = opts.get(b'output')
2666 if cmdutil.isstdiofilename(fntemplate):
2670 if cmdutil.isstdiofilename(fntemplate):
2667 fntemplate = b''
2671 fntemplate = b''
2668
2672
2669 if fntemplate:
2673 if fntemplate:
2670 fm = formatter.nullformatter(ui, b'export', opts)
2674 fm = formatter.nullformatter(ui, b'export', opts)
2671 else:
2675 else:
2672 ui.pager(b'export')
2676 ui.pager(b'export')
2673 fm = ui.formatter(b'export', opts)
2677 fm = ui.formatter(b'export', opts)
2674 with fm:
2678 with fm:
2675 cmdutil.export(
2679 cmdutil.export(
2676 repo,
2680 repo,
2677 revs,
2681 revs,
2678 fm,
2682 fm,
2679 fntemplate=fntemplate,
2683 fntemplate=fntemplate,
2680 switch_parent=opts.get(b'switch_parent'),
2684 switch_parent=opts.get(b'switch_parent'),
2681 opts=patch.diffallopts(ui, opts),
2685 opts=patch.diffallopts(ui, opts),
2682 )
2686 )
2683
2687
2684
2688
2685 @command(
2689 @command(
2686 b'files',
2690 b'files',
2687 [
2691 [
2688 (
2692 (
2689 b'r',
2693 b'r',
2690 b'rev',
2694 b'rev',
2691 b'',
2695 b'',
2692 _(b'search the repository as it is in REV'),
2696 _(b'search the repository as it is in REV'),
2693 _(b'REV'),
2697 _(b'REV'),
2694 ),
2698 ),
2695 (
2699 (
2696 b'0',
2700 b'0',
2697 b'print0',
2701 b'print0',
2698 None,
2702 None,
2699 _(b'end filenames with NUL, for use with xargs'),
2703 _(b'end filenames with NUL, for use with xargs'),
2700 ),
2704 ),
2701 ]
2705 ]
2702 + walkopts
2706 + walkopts
2703 + formatteropts
2707 + formatteropts
2704 + subrepoopts,
2708 + subrepoopts,
2705 _(b'[OPTION]... [FILE]...'),
2709 _(b'[OPTION]... [FILE]...'),
2706 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2710 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2707 intents={INTENT_READONLY},
2711 intents={INTENT_READONLY},
2708 )
2712 )
2709 def files(ui, repo, *pats, **opts):
2713 def files(ui, repo, *pats, **opts):
2710 """list tracked files
2714 """list tracked files
2711
2715
2712 Print files under Mercurial control in the working directory or
2716 Print files under Mercurial control in the working directory or
2713 specified revision for given files (excluding removed files).
2717 specified revision for given files (excluding removed files).
2714 Files can be specified as filenames or filesets.
2718 Files can be specified as filenames or filesets.
2715
2719
2716 If no files are given to match, this command prints the names
2720 If no files are given to match, this command prints the names
2717 of all files under Mercurial control.
2721 of all files under Mercurial control.
2718
2722
2719 .. container:: verbose
2723 .. container:: verbose
2720
2724
2721 Template:
2725 Template:
2722
2726
2723 The following keywords are supported in addition to the common template
2727 The following keywords are supported in addition to the common template
2724 keywords and functions. See also :hg:`help templates`.
2728 keywords and functions. See also :hg:`help templates`.
2725
2729
2726 :flags: String. Character denoting file's symlink and executable bits.
2730 :flags: String. Character denoting file's symlink and executable bits.
2727 :path: String. Repository-absolute path of the file.
2731 :path: String. Repository-absolute path of the file.
2728 :size: Integer. Size of the file in bytes.
2732 :size: Integer. Size of the file in bytes.
2729
2733
2730 Examples:
2734 Examples:
2731
2735
2732 - list all files under the current directory::
2736 - list all files under the current directory::
2733
2737
2734 hg files .
2738 hg files .
2735
2739
2736 - shows sizes and flags for current revision::
2740 - shows sizes and flags for current revision::
2737
2741
2738 hg files -vr .
2742 hg files -vr .
2739
2743
2740 - list all files named README::
2744 - list all files named README::
2741
2745
2742 hg files -I "**/README"
2746 hg files -I "**/README"
2743
2747
2744 - list all binary files::
2748 - list all binary files::
2745
2749
2746 hg files "set:binary()"
2750 hg files "set:binary()"
2747
2751
2748 - find files containing a regular expression::
2752 - find files containing a regular expression::
2749
2753
2750 hg files "set:grep('bob')"
2754 hg files "set:grep('bob')"
2751
2755
2752 - search tracked file contents with xargs and grep::
2756 - search tracked file contents with xargs and grep::
2753
2757
2754 hg files -0 | xargs -0 grep foo
2758 hg files -0 | xargs -0 grep foo
2755
2759
2756 See :hg:`help patterns` and :hg:`help filesets` for more information
2760 See :hg:`help patterns` and :hg:`help filesets` for more information
2757 on specifying file patterns.
2761 on specifying file patterns.
2758
2762
2759 Returns 0 if a match is found, 1 otherwise.
2763 Returns 0 if a match is found, 1 otherwise.
2760
2764
2761 """
2765 """
2762
2766
2763 opts = pycompat.byteskwargs(opts)
2767 opts = pycompat.byteskwargs(opts)
2764 rev = opts.get(b'rev')
2768 rev = opts.get(b'rev')
2765 if rev:
2769 if rev:
2766 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2770 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2767 ctx = scmutil.revsingle(repo, rev, None)
2771 ctx = scmutil.revsingle(repo, rev, None)
2768
2772
2769 end = b'\n'
2773 end = b'\n'
2770 if opts.get(b'print0'):
2774 if opts.get(b'print0'):
2771 end = b'\0'
2775 end = b'\0'
2772 fmt = b'%s' + end
2776 fmt = b'%s' + end
2773
2777
2774 m = scmutil.match(ctx, pats, opts)
2778 m = scmutil.match(ctx, pats, opts)
2775 ui.pager(b'files')
2779 ui.pager(b'files')
2776 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2780 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2777 with ui.formatter(b'files', opts) as fm:
2781 with ui.formatter(b'files', opts) as fm:
2778 return cmdutil.files(
2782 return cmdutil.files(
2779 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2783 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2780 )
2784 )
2781
2785
2782
2786
2783 @command(
2787 @command(
2784 b'forget',
2788 b'forget',
2785 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2789 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2786 + walkopts
2790 + walkopts
2787 + dryrunopts,
2791 + dryrunopts,
2788 _(b'[OPTION]... FILE...'),
2792 _(b'[OPTION]... FILE...'),
2789 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2793 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2790 helpbasic=True,
2794 helpbasic=True,
2791 inferrepo=True,
2795 inferrepo=True,
2792 )
2796 )
2793 def forget(ui, repo, *pats, **opts):
2797 def forget(ui, repo, *pats, **opts):
2794 """forget the specified files on the next commit
2798 """forget the specified files on the next commit
2795
2799
2796 Mark the specified files so they will no longer be tracked
2800 Mark the specified files so they will no longer be tracked
2797 after the next commit.
2801 after the next commit.
2798
2802
2799 This only removes files from the current branch, not from the
2803 This only removes files from the current branch, not from the
2800 entire project history, and it does not delete them from the
2804 entire project history, and it does not delete them from the
2801 working directory.
2805 working directory.
2802
2806
2803 To delete the file from the working directory, see :hg:`remove`.
2807 To delete the file from the working directory, see :hg:`remove`.
2804
2808
2805 To undo a forget before the next commit, see :hg:`add`.
2809 To undo a forget before the next commit, see :hg:`add`.
2806
2810
2807 .. container:: verbose
2811 .. container:: verbose
2808
2812
2809 Examples:
2813 Examples:
2810
2814
2811 - forget newly-added binary files::
2815 - forget newly-added binary files::
2812
2816
2813 hg forget "set:added() and binary()"
2817 hg forget "set:added() and binary()"
2814
2818
2815 - forget files that would be excluded by .hgignore::
2819 - forget files that would be excluded by .hgignore::
2816
2820
2817 hg forget "set:hgignore()"
2821 hg forget "set:hgignore()"
2818
2822
2819 Returns 0 on success.
2823 Returns 0 on success.
2820 """
2824 """
2821
2825
2822 opts = pycompat.byteskwargs(opts)
2826 opts = pycompat.byteskwargs(opts)
2823 if not pats:
2827 if not pats:
2824 raise error.Abort(_(b'no files specified'))
2828 raise error.Abort(_(b'no files specified'))
2825
2829
2826 m = scmutil.match(repo[None], pats, opts)
2830 m = scmutil.match(repo[None], pats, opts)
2827 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2831 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2828 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2832 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2829 rejected = cmdutil.forget(
2833 rejected = cmdutil.forget(
2830 ui,
2834 ui,
2831 repo,
2835 repo,
2832 m,
2836 m,
2833 prefix=b"",
2837 prefix=b"",
2834 uipathfn=uipathfn,
2838 uipathfn=uipathfn,
2835 explicitonly=False,
2839 explicitonly=False,
2836 dryrun=dryrun,
2840 dryrun=dryrun,
2837 interactive=interactive,
2841 interactive=interactive,
2838 )[0]
2842 )[0]
2839 return rejected and 1 or 0
2843 return rejected and 1 or 0
2840
2844
2841
2845
2842 @command(
2846 @command(
2843 b'graft',
2847 b'graft',
2844 [
2848 [
2845 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2849 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2846 (
2850 (
2847 b'',
2851 b'',
2848 b'base',
2852 b'base',
2849 b'',
2853 b'',
2850 _(b'base revision when doing the graft merge (ADVANCED)'),
2854 _(b'base revision when doing the graft merge (ADVANCED)'),
2851 _(b'REV'),
2855 _(b'REV'),
2852 ),
2856 ),
2853 (b'c', b'continue', False, _(b'resume interrupted graft')),
2857 (b'c', b'continue', False, _(b'resume interrupted graft')),
2854 (b'', b'stop', False, _(b'stop interrupted graft')),
2858 (b'', b'stop', False, _(b'stop interrupted graft')),
2855 (b'', b'abort', False, _(b'abort interrupted graft')),
2859 (b'', b'abort', False, _(b'abort interrupted graft')),
2856 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2860 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2857 (b'', b'log', None, _(b'append graft info to log message')),
2861 (b'', b'log', None, _(b'append graft info to log message')),
2858 (
2862 (
2859 b'',
2863 b'',
2860 b'no-commit',
2864 b'no-commit',
2861 None,
2865 None,
2862 _(b"don't commit, just apply the changes in working directory"),
2866 _(b"don't commit, just apply the changes in working directory"),
2863 ),
2867 ),
2864 (b'f', b'force', False, _(b'force graft')),
2868 (b'f', b'force', False, _(b'force graft')),
2865 (
2869 (
2866 b'D',
2870 b'D',
2867 b'currentdate',
2871 b'currentdate',
2868 False,
2872 False,
2869 _(b'record the current date as commit date'),
2873 _(b'record the current date as commit date'),
2870 ),
2874 ),
2871 (
2875 (
2872 b'U',
2876 b'U',
2873 b'currentuser',
2877 b'currentuser',
2874 False,
2878 False,
2875 _(b'record the current user as committer'),
2879 _(b'record the current user as committer'),
2876 ),
2880 ),
2877 ]
2881 ]
2878 + commitopts2
2882 + commitopts2
2879 + mergetoolopts
2883 + mergetoolopts
2880 + dryrunopts,
2884 + dryrunopts,
2881 _(b'[OPTION]... [-r REV]... REV...'),
2885 _(b'[OPTION]... [-r REV]... REV...'),
2882 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2886 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2883 )
2887 )
2884 def graft(ui, repo, *revs, **opts):
2888 def graft(ui, repo, *revs, **opts):
2885 '''copy changes from other branches onto the current branch
2889 '''copy changes from other branches onto the current branch
2886
2890
2887 This command uses Mercurial's merge logic to copy individual
2891 This command uses Mercurial's merge logic to copy individual
2888 changes from other branches without merging branches in the
2892 changes from other branches without merging branches in the
2889 history graph. This is sometimes known as 'backporting' or
2893 history graph. This is sometimes known as 'backporting' or
2890 'cherry-picking'. By default, graft will copy user, date, and
2894 'cherry-picking'. By default, graft will copy user, date, and
2891 description from the source changesets.
2895 description from the source changesets.
2892
2896
2893 Changesets that are ancestors of the current revision, that have
2897 Changesets that are ancestors of the current revision, that have
2894 already been grafted, or that are merges will be skipped.
2898 already been grafted, or that are merges will be skipped.
2895
2899
2896 If --log is specified, log messages will have a comment appended
2900 If --log is specified, log messages will have a comment appended
2897 of the form::
2901 of the form::
2898
2902
2899 (grafted from CHANGESETHASH)
2903 (grafted from CHANGESETHASH)
2900
2904
2901 If --force is specified, revisions will be grafted even if they
2905 If --force is specified, revisions will be grafted even if they
2902 are already ancestors of, or have been grafted to, the destination.
2906 are already ancestors of, or have been grafted to, the destination.
2903 This is useful when the revisions have since been backed out.
2907 This is useful when the revisions have since been backed out.
2904
2908
2905 If a graft merge results in conflicts, the graft process is
2909 If a graft merge results in conflicts, the graft process is
2906 interrupted so that the current merge can be manually resolved.
2910 interrupted so that the current merge can be manually resolved.
2907 Once all conflicts are addressed, the graft process can be
2911 Once all conflicts are addressed, the graft process can be
2908 continued with the -c/--continue option.
2912 continued with the -c/--continue option.
2909
2913
2910 The -c/--continue option reapplies all the earlier options.
2914 The -c/--continue option reapplies all the earlier options.
2911
2915
2912 .. container:: verbose
2916 .. container:: verbose
2913
2917
2914 The --base option exposes more of how graft internally uses merge with a
2918 The --base option exposes more of how graft internally uses merge with a
2915 custom base revision. --base can be used to specify another ancestor than
2919 custom base revision. --base can be used to specify another ancestor than
2916 the first and only parent.
2920 the first and only parent.
2917
2921
2918 The command::
2922 The command::
2919
2923
2920 hg graft -r 345 --base 234
2924 hg graft -r 345 --base 234
2921
2925
2922 is thus pretty much the same as::
2926 is thus pretty much the same as::
2923
2927
2924 hg diff -r 234 -r 345 | hg import
2928 hg diff -r 234 -r 345 | hg import
2925
2929
2926 but using merge to resolve conflicts and track moved files.
2930 but using merge to resolve conflicts and track moved files.
2927
2931
2928 The result of a merge can thus be backported as a single commit by
2932 The result of a merge can thus be backported as a single commit by
2929 specifying one of the merge parents as base, and thus effectively
2933 specifying one of the merge parents as base, and thus effectively
2930 grafting the changes from the other side.
2934 grafting the changes from the other side.
2931
2935
2932 It is also possible to collapse multiple changesets and clean up history
2936 It is also possible to collapse multiple changesets and clean up history
2933 by specifying another ancestor as base, much like rebase --collapse
2937 by specifying another ancestor as base, much like rebase --collapse
2934 --keep.
2938 --keep.
2935
2939
2936 The commit message can be tweaked after the fact using commit --amend .
2940 The commit message can be tweaked after the fact using commit --amend .
2937
2941
2938 For using non-ancestors as the base to backout changes, see the backout
2942 For using non-ancestors as the base to backout changes, see the backout
2939 command and the hidden --parent option.
2943 command and the hidden --parent option.
2940
2944
2941 .. container:: verbose
2945 .. container:: verbose
2942
2946
2943 Examples:
2947 Examples:
2944
2948
2945 - copy a single change to the stable branch and edit its description::
2949 - copy a single change to the stable branch and edit its description::
2946
2950
2947 hg update stable
2951 hg update stable
2948 hg graft --edit 9393
2952 hg graft --edit 9393
2949
2953
2950 - graft a range of changesets with one exception, updating dates::
2954 - graft a range of changesets with one exception, updating dates::
2951
2955
2952 hg graft -D "2085::2093 and not 2091"
2956 hg graft -D "2085::2093 and not 2091"
2953
2957
2954 - continue a graft after resolving conflicts::
2958 - continue a graft after resolving conflicts::
2955
2959
2956 hg graft -c
2960 hg graft -c
2957
2961
2958 - show the source of a grafted changeset::
2962 - show the source of a grafted changeset::
2959
2963
2960 hg log --debug -r .
2964 hg log --debug -r .
2961
2965
2962 - show revisions sorted by date::
2966 - show revisions sorted by date::
2963
2967
2964 hg log -r "sort(all(), date)"
2968 hg log -r "sort(all(), date)"
2965
2969
2966 - backport the result of a merge as a single commit::
2970 - backport the result of a merge as a single commit::
2967
2971
2968 hg graft -r 123 --base 123^
2972 hg graft -r 123 --base 123^
2969
2973
2970 - land a feature branch as one changeset::
2974 - land a feature branch as one changeset::
2971
2975
2972 hg up -cr default
2976 hg up -cr default
2973 hg graft -r featureX --base "ancestor('featureX', 'default')"
2977 hg graft -r featureX --base "ancestor('featureX', 'default')"
2974
2978
2975 See :hg:`help revisions` for more about specifying revisions.
2979 See :hg:`help revisions` for more about specifying revisions.
2976
2980
2977 Returns 0 on successful completion, 1 if there are unresolved files.
2981 Returns 0 on successful completion, 1 if there are unresolved files.
2978 '''
2982 '''
2979 with repo.wlock():
2983 with repo.wlock():
2980 return _dograft(ui, repo, *revs, **opts)
2984 return _dograft(ui, repo, *revs, **opts)
2981
2985
2982
2986
2983 def _dograft(ui, repo, *revs, **opts):
2987 def _dograft(ui, repo, *revs, **opts):
2984 opts = pycompat.byteskwargs(opts)
2988 opts = pycompat.byteskwargs(opts)
2985 if revs and opts.get(b'rev'):
2989 if revs and opts.get(b'rev'):
2986 ui.warn(
2990 ui.warn(
2987 _(
2991 _(
2988 b'warning: inconsistent use of --rev might give unexpected '
2992 b'warning: inconsistent use of --rev might give unexpected '
2989 b'revision ordering!\n'
2993 b'revision ordering!\n'
2990 )
2994 )
2991 )
2995 )
2992
2996
2993 revs = list(revs)
2997 revs = list(revs)
2994 revs.extend(opts.get(b'rev'))
2998 revs.extend(opts.get(b'rev'))
2995 # a dict of data to be stored in state file
2999 # a dict of data to be stored in state file
2996 statedata = {}
3000 statedata = {}
2997 # list of new nodes created by ongoing graft
3001 # list of new nodes created by ongoing graft
2998 statedata[b'newnodes'] = []
3002 statedata[b'newnodes'] = []
2999
3003
3000 cmdutil.resolvecommitoptions(ui, opts)
3004 cmdutil.resolvecommitoptions(ui, opts)
3001
3005
3002 editor = cmdutil.getcommiteditor(
3006 editor = cmdutil.getcommiteditor(
3003 editform=b'graft', **pycompat.strkwargs(opts)
3007 editform=b'graft', **pycompat.strkwargs(opts)
3004 )
3008 )
3005
3009
3006 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3010 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3007
3011
3008 cont = False
3012 cont = False
3009 if opts.get(b'no_commit'):
3013 if opts.get(b'no_commit'):
3010 cmdutil.check_incompatible_arguments(
3014 cmdutil.check_incompatible_arguments(
3011 opts,
3015 opts,
3012 b'no_commit',
3016 b'no_commit',
3013 [b'edit', b'currentuser', b'currentdate', b'log'],
3017 [b'edit', b'currentuser', b'currentdate', b'log'],
3014 )
3018 )
3015
3019
3016 graftstate = statemod.cmdstate(repo, b'graftstate')
3020 graftstate = statemod.cmdstate(repo, b'graftstate')
3017
3021
3018 if opts.get(b'stop'):
3022 if opts.get(b'stop'):
3019 cmdutil.check_incompatible_arguments(
3023 cmdutil.check_incompatible_arguments(
3020 opts,
3024 opts,
3021 b'stop',
3025 b'stop',
3022 [
3026 [
3023 b'edit',
3027 b'edit',
3024 b'log',
3028 b'log',
3025 b'user',
3029 b'user',
3026 b'date',
3030 b'date',
3027 b'currentdate',
3031 b'currentdate',
3028 b'currentuser',
3032 b'currentuser',
3029 b'rev',
3033 b'rev',
3030 ],
3034 ],
3031 )
3035 )
3032 return _stopgraft(ui, repo, graftstate)
3036 return _stopgraft(ui, repo, graftstate)
3033 elif opts.get(b'abort'):
3037 elif opts.get(b'abort'):
3034 cmdutil.check_incompatible_arguments(
3038 cmdutil.check_incompatible_arguments(
3035 opts,
3039 opts,
3036 b'abort',
3040 b'abort',
3037 [
3041 [
3038 b'edit',
3042 b'edit',
3039 b'log',
3043 b'log',
3040 b'user',
3044 b'user',
3041 b'date',
3045 b'date',
3042 b'currentdate',
3046 b'currentdate',
3043 b'currentuser',
3047 b'currentuser',
3044 b'rev',
3048 b'rev',
3045 ],
3049 ],
3046 )
3050 )
3047 return cmdutil.abortgraft(ui, repo, graftstate)
3051 return cmdutil.abortgraft(ui, repo, graftstate)
3048 elif opts.get(b'continue'):
3052 elif opts.get(b'continue'):
3049 cont = True
3053 cont = True
3050 if revs:
3054 if revs:
3051 raise error.Abort(_(b"can't specify --continue and revisions"))
3055 raise error.Abort(_(b"can't specify --continue and revisions"))
3052 # read in unfinished revisions
3056 # read in unfinished revisions
3053 if graftstate.exists():
3057 if graftstate.exists():
3054 statedata = cmdutil.readgraftstate(repo, graftstate)
3058 statedata = cmdutil.readgraftstate(repo, graftstate)
3055 if statedata.get(b'date'):
3059 if statedata.get(b'date'):
3056 opts[b'date'] = statedata[b'date']
3060 opts[b'date'] = statedata[b'date']
3057 if statedata.get(b'user'):
3061 if statedata.get(b'user'):
3058 opts[b'user'] = statedata[b'user']
3062 opts[b'user'] = statedata[b'user']
3059 if statedata.get(b'log'):
3063 if statedata.get(b'log'):
3060 opts[b'log'] = True
3064 opts[b'log'] = True
3061 if statedata.get(b'no_commit'):
3065 if statedata.get(b'no_commit'):
3062 opts[b'no_commit'] = statedata.get(b'no_commit')
3066 opts[b'no_commit'] = statedata.get(b'no_commit')
3063 if statedata.get(b'base'):
3067 if statedata.get(b'base'):
3064 opts[b'base'] = statedata.get(b'base')
3068 opts[b'base'] = statedata.get(b'base')
3065 nodes = statedata[b'nodes']
3069 nodes = statedata[b'nodes']
3066 revs = [repo[node].rev() for node in nodes]
3070 revs = [repo[node].rev() for node in nodes]
3067 else:
3071 else:
3068 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3072 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3069 else:
3073 else:
3070 if not revs:
3074 if not revs:
3071 raise error.Abort(_(b'no revisions specified'))
3075 raise error.Abort(_(b'no revisions specified'))
3072 cmdutil.checkunfinished(repo)
3076 cmdutil.checkunfinished(repo)
3073 cmdutil.bailifchanged(repo)
3077 cmdutil.bailifchanged(repo)
3074 revs = scmutil.revrange(repo, revs)
3078 revs = scmutil.revrange(repo, revs)
3075
3079
3076 skipped = set()
3080 skipped = set()
3077 basectx = None
3081 basectx = None
3078 if opts.get(b'base'):
3082 if opts.get(b'base'):
3079 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3083 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3080 if basectx is None:
3084 if basectx is None:
3081 # check for merges
3085 # check for merges
3082 for rev in repo.revs(b'%ld and merge()', revs):
3086 for rev in repo.revs(b'%ld and merge()', revs):
3083 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3087 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3084 skipped.add(rev)
3088 skipped.add(rev)
3085 revs = [r for r in revs if r not in skipped]
3089 revs = [r for r in revs if r not in skipped]
3086 if not revs:
3090 if not revs:
3087 return -1
3091 return -1
3088 if basectx is not None and len(revs) != 1:
3092 if basectx is not None and len(revs) != 1:
3089 raise error.Abort(_(b'only one revision allowed with --base '))
3093 raise error.Abort(_(b'only one revision allowed with --base '))
3090
3094
3091 # Don't check in the --continue case, in effect retaining --force across
3095 # Don't check in the --continue case, in effect retaining --force across
3092 # --continues. That's because without --force, any revisions we decided to
3096 # --continues. That's because without --force, any revisions we decided to
3093 # skip would have been filtered out here, so they wouldn't have made their
3097 # skip would have been filtered out here, so they wouldn't have made their
3094 # way to the graftstate. With --force, any revisions we would have otherwise
3098 # way to the graftstate. With --force, any revisions we would have otherwise
3095 # skipped would not have been filtered out, and if they hadn't been applied
3099 # skipped would not have been filtered out, and if they hadn't been applied
3096 # already, they'd have been in the graftstate.
3100 # already, they'd have been in the graftstate.
3097 if not (cont or opts.get(b'force')) and basectx is None:
3101 if not (cont or opts.get(b'force')) and basectx is None:
3098 # check for ancestors of dest branch
3102 # check for ancestors of dest branch
3099 ancestors = repo.revs(b'%ld & (::.)', revs)
3103 ancestors = repo.revs(b'%ld & (::.)', revs)
3100 for rev in ancestors:
3104 for rev in ancestors:
3101 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3105 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3102
3106
3103 revs = [r for r in revs if r not in ancestors]
3107 revs = [r for r in revs if r not in ancestors]
3104
3108
3105 if not revs:
3109 if not revs:
3106 return -1
3110 return -1
3107
3111
3108 # analyze revs for earlier grafts
3112 # analyze revs for earlier grafts
3109 ids = {}
3113 ids = {}
3110 for ctx in repo.set(b"%ld", revs):
3114 for ctx in repo.set(b"%ld", revs):
3111 ids[ctx.hex()] = ctx.rev()
3115 ids[ctx.hex()] = ctx.rev()
3112 n = ctx.extra().get(b'source')
3116 n = ctx.extra().get(b'source')
3113 if n:
3117 if n:
3114 ids[n] = ctx.rev()
3118 ids[n] = ctx.rev()
3115
3119
3116 # check ancestors for earlier grafts
3120 # check ancestors for earlier grafts
3117 ui.debug(b'scanning for duplicate grafts\n')
3121 ui.debug(b'scanning for duplicate grafts\n')
3118
3122
3119 # The only changesets we can be sure doesn't contain grafts of any
3123 # The only changesets we can be sure doesn't contain grafts of any
3120 # revs, are the ones that are common ancestors of *all* revs:
3124 # revs, are the ones that are common ancestors of *all* revs:
3121 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3125 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3122 ctx = repo[rev]
3126 ctx = repo[rev]
3123 n = ctx.extra().get(b'source')
3127 n = ctx.extra().get(b'source')
3124 if n in ids:
3128 if n in ids:
3125 try:
3129 try:
3126 r = repo[n].rev()
3130 r = repo[n].rev()
3127 except error.RepoLookupError:
3131 except error.RepoLookupError:
3128 r = None
3132 r = None
3129 if r in revs:
3133 if r in revs:
3130 ui.warn(
3134 ui.warn(
3131 _(
3135 _(
3132 b'skipping revision %d:%s '
3136 b'skipping revision %d:%s '
3133 b'(already grafted to %d:%s)\n'
3137 b'(already grafted to %d:%s)\n'
3134 )
3138 )
3135 % (r, repo[r], rev, ctx)
3139 % (r, repo[r], rev, ctx)
3136 )
3140 )
3137 revs.remove(r)
3141 revs.remove(r)
3138 elif ids[n] in revs:
3142 elif ids[n] in revs:
3139 if r is None:
3143 if r is None:
3140 ui.warn(
3144 ui.warn(
3141 _(
3145 _(
3142 b'skipping already grafted revision %d:%s '
3146 b'skipping already grafted revision %d:%s '
3143 b'(%d:%s also has unknown origin %s)\n'
3147 b'(%d:%s also has unknown origin %s)\n'
3144 )
3148 )
3145 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3149 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3146 )
3150 )
3147 else:
3151 else:
3148 ui.warn(
3152 ui.warn(
3149 _(
3153 _(
3150 b'skipping already grafted revision %d:%s '
3154 b'skipping already grafted revision %d:%s '
3151 b'(%d:%s also has origin %d:%s)\n'
3155 b'(%d:%s also has origin %d:%s)\n'
3152 )
3156 )
3153 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3157 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3154 )
3158 )
3155 revs.remove(ids[n])
3159 revs.remove(ids[n])
3156 elif ctx.hex() in ids:
3160 elif ctx.hex() in ids:
3157 r = ids[ctx.hex()]
3161 r = ids[ctx.hex()]
3158 if r in revs:
3162 if r in revs:
3159 ui.warn(
3163 ui.warn(
3160 _(
3164 _(
3161 b'skipping already grafted revision %d:%s '
3165 b'skipping already grafted revision %d:%s '
3162 b'(was grafted from %d:%s)\n'
3166 b'(was grafted from %d:%s)\n'
3163 )
3167 )
3164 % (r, repo[r], rev, ctx)
3168 % (r, repo[r], rev, ctx)
3165 )
3169 )
3166 revs.remove(r)
3170 revs.remove(r)
3167 if not revs:
3171 if not revs:
3168 return -1
3172 return -1
3169
3173
3170 if opts.get(b'no_commit'):
3174 if opts.get(b'no_commit'):
3171 statedata[b'no_commit'] = True
3175 statedata[b'no_commit'] = True
3172 if opts.get(b'base'):
3176 if opts.get(b'base'):
3173 statedata[b'base'] = opts[b'base']
3177 statedata[b'base'] = opts[b'base']
3174 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3178 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3175 desc = b'%d:%s "%s"' % (
3179 desc = b'%d:%s "%s"' % (
3176 ctx.rev(),
3180 ctx.rev(),
3177 ctx,
3181 ctx,
3178 ctx.description().split(b'\n', 1)[0],
3182 ctx.description().split(b'\n', 1)[0],
3179 )
3183 )
3180 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3184 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3181 if names:
3185 if names:
3182 desc += b' (%s)' % b' '.join(names)
3186 desc += b' (%s)' % b' '.join(names)
3183 ui.status(_(b'grafting %s\n') % desc)
3187 ui.status(_(b'grafting %s\n') % desc)
3184 if opts.get(b'dry_run'):
3188 if opts.get(b'dry_run'):
3185 continue
3189 continue
3186
3190
3187 source = ctx.extra().get(b'source')
3191 source = ctx.extra().get(b'source')
3188 extra = {}
3192 extra = {}
3189 if source:
3193 if source:
3190 extra[b'source'] = source
3194 extra[b'source'] = source
3191 extra[b'intermediate-source'] = ctx.hex()
3195 extra[b'intermediate-source'] = ctx.hex()
3192 else:
3196 else:
3193 extra[b'source'] = ctx.hex()
3197 extra[b'source'] = ctx.hex()
3194 user = ctx.user()
3198 user = ctx.user()
3195 if opts.get(b'user'):
3199 if opts.get(b'user'):
3196 user = opts[b'user']
3200 user = opts[b'user']
3197 statedata[b'user'] = user
3201 statedata[b'user'] = user
3198 date = ctx.date()
3202 date = ctx.date()
3199 if opts.get(b'date'):
3203 if opts.get(b'date'):
3200 date = opts[b'date']
3204 date = opts[b'date']
3201 statedata[b'date'] = date
3205 statedata[b'date'] = date
3202 message = ctx.description()
3206 message = ctx.description()
3203 if opts.get(b'log'):
3207 if opts.get(b'log'):
3204 message += b'\n(grafted from %s)' % ctx.hex()
3208 message += b'\n(grafted from %s)' % ctx.hex()
3205 statedata[b'log'] = True
3209 statedata[b'log'] = True
3206
3210
3207 # we don't merge the first commit when continuing
3211 # we don't merge the first commit when continuing
3208 if not cont:
3212 if not cont:
3209 # perform the graft merge with p1(rev) as 'ancestor'
3213 # perform the graft merge with p1(rev) as 'ancestor'
3210 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3214 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3211 base = ctx.p1() if basectx is None else basectx
3215 base = ctx.p1() if basectx is None else basectx
3212 with ui.configoverride(overrides, b'graft'):
3216 with ui.configoverride(overrides, b'graft'):
3213 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3217 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3214 # report any conflicts
3218 # report any conflicts
3215 if stats.unresolvedcount > 0:
3219 if stats.unresolvedcount > 0:
3216 # write out state for --continue
3220 # write out state for --continue
3217 nodes = [repo[rev].hex() for rev in revs[pos:]]
3221 nodes = [repo[rev].hex() for rev in revs[pos:]]
3218 statedata[b'nodes'] = nodes
3222 statedata[b'nodes'] = nodes
3219 stateversion = 1
3223 stateversion = 1
3220 graftstate.save(stateversion, statedata)
3224 graftstate.save(stateversion, statedata)
3221 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3225 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3222 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3226 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3223 return 1
3227 return 1
3224 else:
3228 else:
3225 cont = False
3229 cont = False
3226
3230
3227 # commit if --no-commit is false
3231 # commit if --no-commit is false
3228 if not opts.get(b'no_commit'):
3232 if not opts.get(b'no_commit'):
3229 node = repo.commit(
3233 node = repo.commit(
3230 text=message, user=user, date=date, extra=extra, editor=editor
3234 text=message, user=user, date=date, extra=extra, editor=editor
3231 )
3235 )
3232 if node is None:
3236 if node is None:
3233 ui.warn(
3237 ui.warn(
3234 _(b'note: graft of %d:%s created no changes to commit\n')
3238 _(b'note: graft of %d:%s created no changes to commit\n')
3235 % (ctx.rev(), ctx)
3239 % (ctx.rev(), ctx)
3236 )
3240 )
3237 # checking that newnodes exist because old state files won't have it
3241 # checking that newnodes exist because old state files won't have it
3238 elif statedata.get(b'newnodes') is not None:
3242 elif statedata.get(b'newnodes') is not None:
3239 statedata[b'newnodes'].append(node)
3243 statedata[b'newnodes'].append(node)
3240
3244
3241 # remove state when we complete successfully
3245 # remove state when we complete successfully
3242 if not opts.get(b'dry_run'):
3246 if not opts.get(b'dry_run'):
3243 graftstate.delete()
3247 graftstate.delete()
3244
3248
3245 return 0
3249 return 0
3246
3250
3247
3251
3248 def _stopgraft(ui, repo, graftstate):
3252 def _stopgraft(ui, repo, graftstate):
3249 """stop the interrupted graft"""
3253 """stop the interrupted graft"""
3250 if not graftstate.exists():
3254 if not graftstate.exists():
3251 raise error.Abort(_(b"no interrupted graft found"))
3255 raise error.Abort(_(b"no interrupted graft found"))
3252 pctx = repo[b'.']
3256 pctx = repo[b'.']
3253 mergemod.clean_update(pctx)
3257 mergemod.clean_update(pctx)
3254 graftstate.delete()
3258 graftstate.delete()
3255 ui.status(_(b"stopped the interrupted graft\n"))
3259 ui.status(_(b"stopped the interrupted graft\n"))
3256 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3260 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3257 return 0
3261 return 0
3258
3262
3259
3263
3260 statemod.addunfinished(
3264 statemod.addunfinished(
3261 b'graft',
3265 b'graft',
3262 fname=b'graftstate',
3266 fname=b'graftstate',
3263 clearable=True,
3267 clearable=True,
3264 stopflag=True,
3268 stopflag=True,
3265 continueflag=True,
3269 continueflag=True,
3266 abortfunc=cmdutil.hgabortgraft,
3270 abortfunc=cmdutil.hgabortgraft,
3267 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3271 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3268 )
3272 )
3269
3273
3270
3274
3271 @command(
3275 @command(
3272 b'grep',
3276 b'grep',
3273 [
3277 [
3274 (b'0', b'print0', None, _(b'end fields with NUL')),
3278 (b'0', b'print0', None, _(b'end fields with NUL')),
3275 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3279 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3276 (
3280 (
3277 b'',
3281 b'',
3278 b'diff',
3282 b'diff',
3279 None,
3283 None,
3280 _(
3284 _(
3281 b'search revision differences for when the pattern was added '
3285 b'search revision differences for when the pattern was added '
3282 b'or removed'
3286 b'or removed'
3283 ),
3287 ),
3284 ),
3288 ),
3285 (b'a', b'text', None, _(b'treat all files as text')),
3289 (b'a', b'text', None, _(b'treat all files as text')),
3286 (
3290 (
3287 b'f',
3291 b'f',
3288 b'follow',
3292 b'follow',
3289 None,
3293 None,
3290 _(
3294 _(
3291 b'follow changeset history,'
3295 b'follow changeset history,'
3292 b' or file history across copies and renames'
3296 b' or file history across copies and renames'
3293 ),
3297 ),
3294 ),
3298 ),
3295 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3299 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3296 (
3300 (
3297 b'l',
3301 b'l',
3298 b'files-with-matches',
3302 b'files-with-matches',
3299 None,
3303 None,
3300 _(b'print only filenames and revisions that match'),
3304 _(b'print only filenames and revisions that match'),
3301 ),
3305 ),
3302 (b'n', b'line-number', None, _(b'print matching line numbers')),
3306 (b'n', b'line-number', None, _(b'print matching line numbers')),
3303 (
3307 (
3304 b'r',
3308 b'r',
3305 b'rev',
3309 b'rev',
3306 [],
3310 [],
3307 _(b'search files changed within revision range'),
3311 _(b'search files changed within revision range'),
3308 _(b'REV'),
3312 _(b'REV'),
3309 ),
3313 ),
3310 (
3314 (
3311 b'',
3315 b'',
3312 b'all-files',
3316 b'all-files',
3313 None,
3317 None,
3314 _(
3318 _(
3315 b'include all files in the changeset while grepping (DEPRECATED)'
3319 b'include all files in the changeset while grepping (DEPRECATED)'
3316 ),
3320 ),
3317 ),
3321 ),
3318 (b'u', b'user', None, _(b'list the author (long with -v)')),
3322 (b'u', b'user', None, _(b'list the author (long with -v)')),
3319 (b'd', b'date', None, _(b'list the date (short with -q)')),
3323 (b'd', b'date', None, _(b'list the date (short with -q)')),
3320 ]
3324 ]
3321 + formatteropts
3325 + formatteropts
3322 + walkopts,
3326 + walkopts,
3323 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3327 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3324 helpcategory=command.CATEGORY_FILE_CONTENTS,
3328 helpcategory=command.CATEGORY_FILE_CONTENTS,
3325 inferrepo=True,
3329 inferrepo=True,
3326 intents={INTENT_READONLY},
3330 intents={INTENT_READONLY},
3327 )
3331 )
3328 def grep(ui, repo, pattern, *pats, **opts):
3332 def grep(ui, repo, pattern, *pats, **opts):
3329 """search for a pattern in specified files
3333 """search for a pattern in specified files
3330
3334
3331 Search the working directory or revision history for a regular
3335 Search the working directory or revision history for a regular
3332 expression in the specified files for the entire repository.
3336 expression in the specified files for the entire repository.
3333
3337
3334 By default, grep searches the repository files in the working
3338 By default, grep searches the repository files in the working
3335 directory and prints the files where it finds a match. To specify
3339 directory and prints the files where it finds a match. To specify
3336 historical revisions instead of the working directory, use the
3340 historical revisions instead of the working directory, use the
3337 --rev flag.
3341 --rev flag.
3338
3342
3339 To search instead historical revision differences that contains a
3343 To search instead historical revision differences that contains a
3340 change in match status ("-" for a match that becomes a non-match,
3344 change in match status ("-" for a match that becomes a non-match,
3341 or "+" for a non-match that becomes a match), use the --diff flag.
3345 or "+" for a non-match that becomes a match), use the --diff flag.
3342
3346
3343 PATTERN can be any Python (roughly Perl-compatible) regular
3347 PATTERN can be any Python (roughly Perl-compatible) regular
3344 expression.
3348 expression.
3345
3349
3346 If no FILEs are specified and the --rev flag isn't supplied, all
3350 If no FILEs are specified and the --rev flag isn't supplied, all
3347 files in the working directory are searched. When using the --rev
3351 files in the working directory are searched. When using the --rev
3348 flag and specifying FILEs, use the --follow argument to also
3352 flag and specifying FILEs, use the --follow argument to also
3349 follow the specified FILEs across renames and copies.
3353 follow the specified FILEs across renames and copies.
3350
3354
3351 .. container:: verbose
3355 .. container:: verbose
3352
3356
3353 Template:
3357 Template:
3354
3358
3355 The following keywords are supported in addition to the common template
3359 The following keywords are supported in addition to the common template
3356 keywords and functions. See also :hg:`help templates`.
3360 keywords and functions. See also :hg:`help templates`.
3357
3361
3358 :change: String. Character denoting insertion ``+`` or removal ``-``.
3362 :change: String. Character denoting insertion ``+`` or removal ``-``.
3359 Available if ``--diff`` is specified.
3363 Available if ``--diff`` is specified.
3360 :lineno: Integer. Line number of the match.
3364 :lineno: Integer. Line number of the match.
3361 :path: String. Repository-absolute path of the file.
3365 :path: String. Repository-absolute path of the file.
3362 :texts: List of text chunks.
3366 :texts: List of text chunks.
3363
3367
3364 And each entry of ``{texts}`` provides the following sub-keywords.
3368 And each entry of ``{texts}`` provides the following sub-keywords.
3365
3369
3366 :matched: Boolean. True if the chunk matches the specified pattern.
3370 :matched: Boolean. True if the chunk matches the specified pattern.
3367 :text: String. Chunk content.
3371 :text: String. Chunk content.
3368
3372
3369 See :hg:`help templates.operators` for the list expansion syntax.
3373 See :hg:`help templates.operators` for the list expansion syntax.
3370
3374
3371 Returns 0 if a match is found, 1 otherwise.
3375 Returns 0 if a match is found, 1 otherwise.
3372
3376
3373 """
3377 """
3374 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3378 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3375 opts = pycompat.byteskwargs(opts)
3379 opts = pycompat.byteskwargs(opts)
3376 diff = opts.get(b'all') or opts.get(b'diff')
3380 diff = opts.get(b'all') or opts.get(b'diff')
3377 follow = opts.get(b'follow')
3381 follow = opts.get(b'follow')
3378 if opts.get(b'all_files') is None and not diff:
3382 if opts.get(b'all_files') is None and not diff:
3379 opts[b'all_files'] = True
3383 opts[b'all_files'] = True
3380 plaingrep = (
3384 plaingrep = (
3381 opts.get(b'all_files')
3385 opts.get(b'all_files')
3382 and not opts.get(b'rev')
3386 and not opts.get(b'rev')
3383 and not opts.get(b'follow')
3387 and not opts.get(b'follow')
3384 )
3388 )
3385 all_files = opts.get(b'all_files')
3389 all_files = opts.get(b'all_files')
3386 if plaingrep:
3390 if plaingrep:
3387 opts[b'rev'] = [b'wdir()']
3391 opts[b'rev'] = [b'wdir()']
3388
3392
3389 reflags = re.M
3393 reflags = re.M
3390 if opts.get(b'ignore_case'):
3394 if opts.get(b'ignore_case'):
3391 reflags |= re.I
3395 reflags |= re.I
3392 try:
3396 try:
3393 regexp = util.re.compile(pattern, reflags)
3397 regexp = util.re.compile(pattern, reflags)
3394 except re.error as inst:
3398 except re.error as inst:
3395 ui.warn(
3399 ui.warn(
3396 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3400 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3397 )
3401 )
3398 return 1
3402 return 1
3399 sep, eol = b':', b'\n'
3403 sep, eol = b':', b'\n'
3400 if opts.get(b'print0'):
3404 if opts.get(b'print0'):
3401 sep = eol = b'\0'
3405 sep = eol = b'\0'
3402
3406
3403 searcher = grepmod.grepsearcher(
3407 searcher = grepmod.grepsearcher(
3404 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3408 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3405 )
3409 )
3406
3410
3407 getfile = searcher._getfile
3411 getfile = searcher._getfile
3408
3412
3409 uipathfn = scmutil.getuipathfn(repo)
3413 uipathfn = scmutil.getuipathfn(repo)
3410
3414
3411 def display(fm, fn, ctx, pstates, states):
3415 def display(fm, fn, ctx, pstates, states):
3412 rev = scmutil.intrev(ctx)
3416 rev = scmutil.intrev(ctx)
3413 if fm.isplain():
3417 if fm.isplain():
3414 formatuser = ui.shortuser
3418 formatuser = ui.shortuser
3415 else:
3419 else:
3416 formatuser = pycompat.bytestr
3420 formatuser = pycompat.bytestr
3417 if ui.quiet:
3421 if ui.quiet:
3418 datefmt = b'%Y-%m-%d'
3422 datefmt = b'%Y-%m-%d'
3419 else:
3423 else:
3420 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3424 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3421 found = False
3425 found = False
3422
3426
3423 @util.cachefunc
3427 @util.cachefunc
3424 def binary():
3428 def binary():
3425 flog = getfile(fn)
3429 flog = getfile(fn)
3426 try:
3430 try:
3427 return stringutil.binary(flog.read(ctx.filenode(fn)))
3431 return stringutil.binary(flog.read(ctx.filenode(fn)))
3428 except error.WdirUnsupported:
3432 except error.WdirUnsupported:
3429 return ctx[fn].isbinary()
3433 return ctx[fn].isbinary()
3430
3434
3431 fieldnamemap = {b'linenumber': b'lineno'}
3435 fieldnamemap = {b'linenumber': b'lineno'}
3432 if diff:
3436 if diff:
3433 iter = grepmod.difflinestates(pstates, states)
3437 iter = grepmod.difflinestates(pstates, states)
3434 else:
3438 else:
3435 iter = [(b'', l) for l in states]
3439 iter = [(b'', l) for l in states]
3436 for change, l in iter:
3440 for change, l in iter:
3437 fm.startitem()
3441 fm.startitem()
3438 fm.context(ctx=ctx)
3442 fm.context(ctx=ctx)
3439 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3443 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3440 fm.plain(uipathfn(fn), label=b'grep.filename')
3444 fm.plain(uipathfn(fn), label=b'grep.filename')
3441
3445
3442 cols = [
3446 cols = [
3443 (b'rev', b'%d', rev, not plaingrep, b''),
3447 (b'rev', b'%d', rev, not plaingrep, b''),
3444 (
3448 (
3445 b'linenumber',
3449 b'linenumber',
3446 b'%d',
3450 b'%d',
3447 l.linenum,
3451 l.linenum,
3448 opts.get(b'line_number'),
3452 opts.get(b'line_number'),
3449 b'',
3453 b'',
3450 ),
3454 ),
3451 ]
3455 ]
3452 if diff:
3456 if diff:
3453 cols.append(
3457 cols.append(
3454 (
3458 (
3455 b'change',
3459 b'change',
3456 b'%s',
3460 b'%s',
3457 change,
3461 change,
3458 True,
3462 True,
3459 b'grep.inserted '
3463 b'grep.inserted '
3460 if change == b'+'
3464 if change == b'+'
3461 else b'grep.deleted ',
3465 else b'grep.deleted ',
3462 )
3466 )
3463 )
3467 )
3464 cols.extend(
3468 cols.extend(
3465 [
3469 [
3466 (
3470 (
3467 b'user',
3471 b'user',
3468 b'%s',
3472 b'%s',
3469 formatuser(ctx.user()),
3473 formatuser(ctx.user()),
3470 opts.get(b'user'),
3474 opts.get(b'user'),
3471 b'',
3475 b'',
3472 ),
3476 ),
3473 (
3477 (
3474 b'date',
3478 b'date',
3475 b'%s',
3479 b'%s',
3476 fm.formatdate(ctx.date(), datefmt),
3480 fm.formatdate(ctx.date(), datefmt),
3477 opts.get(b'date'),
3481 opts.get(b'date'),
3478 b'',
3482 b'',
3479 ),
3483 ),
3480 ]
3484 ]
3481 )
3485 )
3482 for name, fmt, data, cond, extra_label in cols:
3486 for name, fmt, data, cond, extra_label in cols:
3483 if cond:
3487 if cond:
3484 fm.plain(sep, label=b'grep.sep')
3488 fm.plain(sep, label=b'grep.sep')
3485 field = fieldnamemap.get(name, name)
3489 field = fieldnamemap.get(name, name)
3486 label = extra_label + (b'grep.%s' % name)
3490 label = extra_label + (b'grep.%s' % name)
3487 fm.condwrite(cond, field, fmt, data, label=label)
3491 fm.condwrite(cond, field, fmt, data, label=label)
3488 if not opts.get(b'files_with_matches'):
3492 if not opts.get(b'files_with_matches'):
3489 fm.plain(sep, label=b'grep.sep')
3493 fm.plain(sep, label=b'grep.sep')
3490 if not opts.get(b'text') and binary():
3494 if not opts.get(b'text') and binary():
3491 fm.plain(_(b" Binary file matches"))
3495 fm.plain(_(b" Binary file matches"))
3492 else:
3496 else:
3493 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3497 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3494 fm.plain(eol)
3498 fm.plain(eol)
3495 found = True
3499 found = True
3496 if opts.get(b'files_with_matches'):
3500 if opts.get(b'files_with_matches'):
3497 break
3501 break
3498 return found
3502 return found
3499
3503
3500 def displaymatches(fm, l):
3504 def displaymatches(fm, l):
3501 p = 0
3505 p = 0
3502 for s, e in l.findpos(regexp):
3506 for s, e in l.findpos(regexp):
3503 if p < s:
3507 if p < s:
3504 fm.startitem()
3508 fm.startitem()
3505 fm.write(b'text', b'%s', l.line[p:s])
3509 fm.write(b'text', b'%s', l.line[p:s])
3506 fm.data(matched=False)
3510 fm.data(matched=False)
3507 fm.startitem()
3511 fm.startitem()
3508 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3512 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3509 fm.data(matched=True)
3513 fm.data(matched=True)
3510 p = e
3514 p = e
3511 if p < len(l.line):
3515 if p < len(l.line):
3512 fm.startitem()
3516 fm.startitem()
3513 fm.write(b'text', b'%s', l.line[p:])
3517 fm.write(b'text', b'%s', l.line[p:])
3514 fm.data(matched=False)
3518 fm.data(matched=False)
3515 fm.end()
3519 fm.end()
3516
3520
3517 found = False
3521 found = False
3518
3522
3519 wopts = logcmdutil.walkopts(
3523 wopts = logcmdutil.walkopts(
3520 pats=pats,
3524 pats=pats,
3521 opts=opts,
3525 opts=opts,
3522 revspec=opts[b'rev'],
3526 revspec=opts[b'rev'],
3523 include_pats=opts[b'include'],
3527 include_pats=opts[b'include'],
3524 exclude_pats=opts[b'exclude'],
3528 exclude_pats=opts[b'exclude'],
3525 follow=follow,
3529 follow=follow,
3526 force_changelog_traversal=all_files,
3530 force_changelog_traversal=all_files,
3527 filter_revisions_by_pats=not all_files,
3531 filter_revisions_by_pats=not all_files,
3528 )
3532 )
3529 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3533 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3530
3534
3531 ui.pager(b'grep')
3535 ui.pager(b'grep')
3532 fm = ui.formatter(b'grep', opts)
3536 fm = ui.formatter(b'grep', opts)
3533 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3537 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3534 r = display(fm, fn, ctx, pstates, states)
3538 r = display(fm, fn, ctx, pstates, states)
3535 found = found or r
3539 found = found or r
3536 if r and not diff and not all_files:
3540 if r and not diff and not all_files:
3537 searcher.skipfile(fn, ctx.rev())
3541 searcher.skipfile(fn, ctx.rev())
3538 fm.end()
3542 fm.end()
3539
3543
3540 return not found
3544 return not found
3541
3545
3542
3546
3543 @command(
3547 @command(
3544 b'heads',
3548 b'heads',
3545 [
3549 [
3546 (
3550 (
3547 b'r',
3551 b'r',
3548 b'rev',
3552 b'rev',
3549 b'',
3553 b'',
3550 _(b'show only heads which are descendants of STARTREV'),
3554 _(b'show only heads which are descendants of STARTREV'),
3551 _(b'STARTREV'),
3555 _(b'STARTREV'),
3552 ),
3556 ),
3553 (b't', b'topo', False, _(b'show topological heads only')),
3557 (b't', b'topo', False, _(b'show topological heads only')),
3554 (
3558 (
3555 b'a',
3559 b'a',
3556 b'active',
3560 b'active',
3557 False,
3561 False,
3558 _(b'show active branchheads only (DEPRECATED)'),
3562 _(b'show active branchheads only (DEPRECATED)'),
3559 ),
3563 ),
3560 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3564 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3561 ]
3565 ]
3562 + templateopts,
3566 + templateopts,
3563 _(b'[-ct] [-r STARTREV] [REV]...'),
3567 _(b'[-ct] [-r STARTREV] [REV]...'),
3564 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3568 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3565 intents={INTENT_READONLY},
3569 intents={INTENT_READONLY},
3566 )
3570 )
3567 def heads(ui, repo, *branchrevs, **opts):
3571 def heads(ui, repo, *branchrevs, **opts):
3568 """show branch heads
3572 """show branch heads
3569
3573
3570 With no arguments, show all open branch heads in the repository.
3574 With no arguments, show all open branch heads in the repository.
3571 Branch heads are changesets that have no descendants on the
3575 Branch heads are changesets that have no descendants on the
3572 same branch. They are where development generally takes place and
3576 same branch. They are where development generally takes place and
3573 are the usual targets for update and merge operations.
3577 are the usual targets for update and merge operations.
3574
3578
3575 If one or more REVs are given, only open branch heads on the
3579 If one or more REVs are given, only open branch heads on the
3576 branches associated with the specified changesets are shown. This
3580 branches associated with the specified changesets are shown. This
3577 means that you can use :hg:`heads .` to see the heads on the
3581 means that you can use :hg:`heads .` to see the heads on the
3578 currently checked-out branch.
3582 currently checked-out branch.
3579
3583
3580 If -c/--closed is specified, also show branch heads marked closed
3584 If -c/--closed is specified, also show branch heads marked closed
3581 (see :hg:`commit --close-branch`).
3585 (see :hg:`commit --close-branch`).
3582
3586
3583 If STARTREV is specified, only those heads that are descendants of
3587 If STARTREV is specified, only those heads that are descendants of
3584 STARTREV will be displayed.
3588 STARTREV will be displayed.
3585
3589
3586 If -t/--topo is specified, named branch mechanics will be ignored and only
3590 If -t/--topo is specified, named branch mechanics will be ignored and only
3587 topological heads (changesets with no children) will be shown.
3591 topological heads (changesets with no children) will be shown.
3588
3592
3589 Returns 0 if matching heads are found, 1 if not.
3593 Returns 0 if matching heads are found, 1 if not.
3590 """
3594 """
3591
3595
3592 opts = pycompat.byteskwargs(opts)
3596 opts = pycompat.byteskwargs(opts)
3593 start = None
3597 start = None
3594 rev = opts.get(b'rev')
3598 rev = opts.get(b'rev')
3595 if rev:
3599 if rev:
3596 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3600 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3597 start = scmutil.revsingle(repo, rev, None).node()
3601 start = scmutil.revsingle(repo, rev, None).node()
3598
3602
3599 if opts.get(b'topo'):
3603 if opts.get(b'topo'):
3600 heads = [repo[h] for h in repo.heads(start)]
3604 heads = [repo[h] for h in repo.heads(start)]
3601 else:
3605 else:
3602 heads = []
3606 heads = []
3603 for branch in repo.branchmap():
3607 for branch in repo.branchmap():
3604 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3608 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3605 heads = [repo[h] for h in heads]
3609 heads = [repo[h] for h in heads]
3606
3610
3607 if branchrevs:
3611 if branchrevs:
3608 branches = {
3612 branches = {
3609 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3613 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3610 }
3614 }
3611 heads = [h for h in heads if h.branch() in branches]
3615 heads = [h for h in heads if h.branch() in branches]
3612
3616
3613 if opts.get(b'active') and branchrevs:
3617 if opts.get(b'active') and branchrevs:
3614 dagheads = repo.heads(start)
3618 dagheads = repo.heads(start)
3615 heads = [h for h in heads if h.node() in dagheads]
3619 heads = [h for h in heads if h.node() in dagheads]
3616
3620
3617 if branchrevs:
3621 if branchrevs:
3618 haveheads = {h.branch() for h in heads}
3622 haveheads = {h.branch() for h in heads}
3619 if branches - haveheads:
3623 if branches - haveheads:
3620 headless = b', '.join(b for b in branches - haveheads)
3624 headless = b', '.join(b for b in branches - haveheads)
3621 msg = _(b'no open branch heads found on branches %s')
3625 msg = _(b'no open branch heads found on branches %s')
3622 if opts.get(b'rev'):
3626 if opts.get(b'rev'):
3623 msg += _(b' (started at %s)') % opts[b'rev']
3627 msg += _(b' (started at %s)') % opts[b'rev']
3624 ui.warn((msg + b'\n') % headless)
3628 ui.warn((msg + b'\n') % headless)
3625
3629
3626 if not heads:
3630 if not heads:
3627 return 1
3631 return 1
3628
3632
3629 ui.pager(b'heads')
3633 ui.pager(b'heads')
3630 heads = sorted(heads, key=lambda x: -(x.rev()))
3634 heads = sorted(heads, key=lambda x: -(x.rev()))
3631 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3635 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3632 for ctx in heads:
3636 for ctx in heads:
3633 displayer.show(ctx)
3637 displayer.show(ctx)
3634 displayer.close()
3638 displayer.close()
3635
3639
3636
3640
3637 @command(
3641 @command(
3638 b'help',
3642 b'help',
3639 [
3643 [
3640 (b'e', b'extension', None, _(b'show only help for extensions')),
3644 (b'e', b'extension', None, _(b'show only help for extensions')),
3641 (b'c', b'command', None, _(b'show only help for commands')),
3645 (b'c', b'command', None, _(b'show only help for commands')),
3642 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3646 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3643 (
3647 (
3644 b's',
3648 b's',
3645 b'system',
3649 b'system',
3646 [],
3650 [],
3647 _(b'show help for specific platform(s)'),
3651 _(b'show help for specific platform(s)'),
3648 _(b'PLATFORM'),
3652 _(b'PLATFORM'),
3649 ),
3653 ),
3650 ],
3654 ],
3651 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3655 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3652 helpcategory=command.CATEGORY_HELP,
3656 helpcategory=command.CATEGORY_HELP,
3653 norepo=True,
3657 norepo=True,
3654 intents={INTENT_READONLY},
3658 intents={INTENT_READONLY},
3655 )
3659 )
3656 def help_(ui, name=None, **opts):
3660 def help_(ui, name=None, **opts):
3657 """show help for a given topic or a help overview
3661 """show help for a given topic or a help overview
3658
3662
3659 With no arguments, print a list of commands with short help messages.
3663 With no arguments, print a list of commands with short help messages.
3660
3664
3661 Given a topic, extension, or command name, print help for that
3665 Given a topic, extension, or command name, print help for that
3662 topic.
3666 topic.
3663
3667
3664 Returns 0 if successful.
3668 Returns 0 if successful.
3665 """
3669 """
3666
3670
3667 keep = opts.get('system') or []
3671 keep = opts.get('system') or []
3668 if len(keep) == 0:
3672 if len(keep) == 0:
3669 if pycompat.sysplatform.startswith(b'win'):
3673 if pycompat.sysplatform.startswith(b'win'):
3670 keep.append(b'windows')
3674 keep.append(b'windows')
3671 elif pycompat.sysplatform == b'OpenVMS':
3675 elif pycompat.sysplatform == b'OpenVMS':
3672 keep.append(b'vms')
3676 keep.append(b'vms')
3673 elif pycompat.sysplatform == b'plan9':
3677 elif pycompat.sysplatform == b'plan9':
3674 keep.append(b'plan9')
3678 keep.append(b'plan9')
3675 else:
3679 else:
3676 keep.append(b'unix')
3680 keep.append(b'unix')
3677 keep.append(pycompat.sysplatform.lower())
3681 keep.append(pycompat.sysplatform.lower())
3678 if ui.verbose:
3682 if ui.verbose:
3679 keep.append(b'verbose')
3683 keep.append(b'verbose')
3680
3684
3681 commands = sys.modules[__name__]
3685 commands = sys.modules[__name__]
3682 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3686 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3683 ui.pager(b'help')
3687 ui.pager(b'help')
3684 ui.write(formatted)
3688 ui.write(formatted)
3685
3689
3686
3690
3687 @command(
3691 @command(
3688 b'identify|id',
3692 b'identify|id',
3689 [
3693 [
3690 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3694 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3691 (b'n', b'num', None, _(b'show local revision number')),
3695 (b'n', b'num', None, _(b'show local revision number')),
3692 (b'i', b'id', None, _(b'show global revision id')),
3696 (b'i', b'id', None, _(b'show global revision id')),
3693 (b'b', b'branch', None, _(b'show branch')),
3697 (b'b', b'branch', None, _(b'show branch')),
3694 (b't', b'tags', None, _(b'show tags')),
3698 (b't', b'tags', None, _(b'show tags')),
3695 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3699 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3696 ]
3700 ]
3697 + remoteopts
3701 + remoteopts
3698 + formatteropts,
3702 + formatteropts,
3699 _(b'[-nibtB] [-r REV] [SOURCE]'),
3703 _(b'[-nibtB] [-r REV] [SOURCE]'),
3700 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3704 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3701 optionalrepo=True,
3705 optionalrepo=True,
3702 intents={INTENT_READONLY},
3706 intents={INTENT_READONLY},
3703 )
3707 )
3704 def identify(
3708 def identify(
3705 ui,
3709 ui,
3706 repo,
3710 repo,
3707 source=None,
3711 source=None,
3708 rev=None,
3712 rev=None,
3709 num=None,
3713 num=None,
3710 id=None,
3714 id=None,
3711 branch=None,
3715 branch=None,
3712 tags=None,
3716 tags=None,
3713 bookmarks=None,
3717 bookmarks=None,
3714 **opts
3718 **opts
3715 ):
3719 ):
3716 """identify the working directory or specified revision
3720 """identify the working directory or specified revision
3717
3721
3718 Print a summary identifying the repository state at REV using one or
3722 Print a summary identifying the repository state at REV using one or
3719 two parent hash identifiers, followed by a "+" if the working
3723 two parent hash identifiers, followed by a "+" if the working
3720 directory has uncommitted changes, the branch name (if not default),
3724 directory has uncommitted changes, the branch name (if not default),
3721 a list of tags, and a list of bookmarks.
3725 a list of tags, and a list of bookmarks.
3722
3726
3723 When REV is not given, print a summary of the current state of the
3727 When REV is not given, print a summary of the current state of the
3724 repository including the working directory. Specify -r. to get information
3728 repository including the working directory. Specify -r. to get information
3725 of the working directory parent without scanning uncommitted changes.
3729 of the working directory parent without scanning uncommitted changes.
3726
3730
3727 Specifying a path to a repository root or Mercurial bundle will
3731 Specifying a path to a repository root or Mercurial bundle will
3728 cause lookup to operate on that repository/bundle.
3732 cause lookup to operate on that repository/bundle.
3729
3733
3730 .. container:: verbose
3734 .. container:: verbose
3731
3735
3732 Template:
3736 Template:
3733
3737
3734 The following keywords are supported in addition to the common template
3738 The following keywords are supported in addition to the common template
3735 keywords and functions. See also :hg:`help templates`.
3739 keywords and functions. See also :hg:`help templates`.
3736
3740
3737 :dirty: String. Character ``+`` denoting if the working directory has
3741 :dirty: String. Character ``+`` denoting if the working directory has
3738 uncommitted changes.
3742 uncommitted changes.
3739 :id: String. One or two nodes, optionally followed by ``+``.
3743 :id: String. One or two nodes, optionally followed by ``+``.
3740 :parents: List of strings. Parent nodes of the changeset.
3744 :parents: List of strings. Parent nodes of the changeset.
3741
3745
3742 Examples:
3746 Examples:
3743
3747
3744 - generate a build identifier for the working directory::
3748 - generate a build identifier for the working directory::
3745
3749
3746 hg id --id > build-id.dat
3750 hg id --id > build-id.dat
3747
3751
3748 - find the revision corresponding to a tag::
3752 - find the revision corresponding to a tag::
3749
3753
3750 hg id -n -r 1.3
3754 hg id -n -r 1.3
3751
3755
3752 - check the most recent revision of a remote repository::
3756 - check the most recent revision of a remote repository::
3753
3757
3754 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3758 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3755
3759
3756 See :hg:`log` for generating more information about specific revisions,
3760 See :hg:`log` for generating more information about specific revisions,
3757 including full hash identifiers.
3761 including full hash identifiers.
3758
3762
3759 Returns 0 if successful.
3763 Returns 0 if successful.
3760 """
3764 """
3761
3765
3762 opts = pycompat.byteskwargs(opts)
3766 opts = pycompat.byteskwargs(opts)
3763 if not repo and not source:
3767 if not repo and not source:
3764 raise error.Abort(
3768 raise error.Abort(
3765 _(b"there is no Mercurial repository here (.hg not found)")
3769 _(b"there is no Mercurial repository here (.hg not found)")
3766 )
3770 )
3767
3771
3768 default = not (num or id or branch or tags or bookmarks)
3772 default = not (num or id or branch or tags or bookmarks)
3769 output = []
3773 output = []
3770 revs = []
3774 revs = []
3771
3775
3772 if source:
3776 if source:
3773 source, branches = hg.parseurl(ui.expandpath(source))
3777 source, branches = hg.parseurl(ui.expandpath(source))
3774 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3778 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3775 repo = peer.local()
3779 repo = peer.local()
3776 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3780 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3777
3781
3778 fm = ui.formatter(b'identify', opts)
3782 fm = ui.formatter(b'identify', opts)
3779 fm.startitem()
3783 fm.startitem()
3780
3784
3781 if not repo:
3785 if not repo:
3782 if num or branch or tags:
3786 if num or branch or tags:
3783 raise error.Abort(
3787 raise error.Abort(
3784 _(b"can't query remote revision number, branch, or tags")
3788 _(b"can't query remote revision number, branch, or tags")
3785 )
3789 )
3786 if not rev and revs:
3790 if not rev and revs:
3787 rev = revs[0]
3791 rev = revs[0]
3788 if not rev:
3792 if not rev:
3789 rev = b"tip"
3793 rev = b"tip"
3790
3794
3791 remoterev = peer.lookup(rev)
3795 remoterev = peer.lookup(rev)
3792 hexrev = fm.hexfunc(remoterev)
3796 hexrev = fm.hexfunc(remoterev)
3793 if default or id:
3797 if default or id:
3794 output = [hexrev]
3798 output = [hexrev]
3795 fm.data(id=hexrev)
3799 fm.data(id=hexrev)
3796
3800
3797 @util.cachefunc
3801 @util.cachefunc
3798 def getbms():
3802 def getbms():
3799 bms = []
3803 bms = []
3800
3804
3801 if b'bookmarks' in peer.listkeys(b'namespaces'):
3805 if b'bookmarks' in peer.listkeys(b'namespaces'):
3802 hexremoterev = hex(remoterev)
3806 hexremoterev = hex(remoterev)
3803 bms = [
3807 bms = [
3804 bm
3808 bm
3805 for bm, bmr in pycompat.iteritems(
3809 for bm, bmr in pycompat.iteritems(
3806 peer.listkeys(b'bookmarks')
3810 peer.listkeys(b'bookmarks')
3807 )
3811 )
3808 if bmr == hexremoterev
3812 if bmr == hexremoterev
3809 ]
3813 ]
3810
3814
3811 return sorted(bms)
3815 return sorted(bms)
3812
3816
3813 if fm.isplain():
3817 if fm.isplain():
3814 if bookmarks:
3818 if bookmarks:
3815 output.extend(getbms())
3819 output.extend(getbms())
3816 elif default and not ui.quiet:
3820 elif default and not ui.quiet:
3817 # multiple bookmarks for a single parent separated by '/'
3821 # multiple bookmarks for a single parent separated by '/'
3818 bm = b'/'.join(getbms())
3822 bm = b'/'.join(getbms())
3819 if bm:
3823 if bm:
3820 output.append(bm)
3824 output.append(bm)
3821 else:
3825 else:
3822 fm.data(node=hex(remoterev))
3826 fm.data(node=hex(remoterev))
3823 if bookmarks or b'bookmarks' in fm.datahint():
3827 if bookmarks or b'bookmarks' in fm.datahint():
3824 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3828 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3825 else:
3829 else:
3826 if rev:
3830 if rev:
3827 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3831 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3828 ctx = scmutil.revsingle(repo, rev, None)
3832 ctx = scmutil.revsingle(repo, rev, None)
3829
3833
3830 if ctx.rev() is None:
3834 if ctx.rev() is None:
3831 ctx = repo[None]
3835 ctx = repo[None]
3832 parents = ctx.parents()
3836 parents = ctx.parents()
3833 taglist = []
3837 taglist = []
3834 for p in parents:
3838 for p in parents:
3835 taglist.extend(p.tags())
3839 taglist.extend(p.tags())
3836
3840
3837 dirty = b""
3841 dirty = b""
3838 if ctx.dirty(missing=True, merge=False, branch=False):
3842 if ctx.dirty(missing=True, merge=False, branch=False):
3839 dirty = b'+'
3843 dirty = b'+'
3840 fm.data(dirty=dirty)
3844 fm.data(dirty=dirty)
3841
3845
3842 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3846 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3843 if default or id:
3847 if default or id:
3844 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3848 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3845 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3849 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3846
3850
3847 if num:
3851 if num:
3848 numoutput = [b"%d" % p.rev() for p in parents]
3852 numoutput = [b"%d" % p.rev() for p in parents]
3849 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3853 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3850
3854
3851 fm.data(
3855 fm.data(
3852 parents=fm.formatlist(
3856 parents=fm.formatlist(
3853 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3857 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3854 )
3858 )
3855 )
3859 )
3856 else:
3860 else:
3857 hexoutput = fm.hexfunc(ctx.node())
3861 hexoutput = fm.hexfunc(ctx.node())
3858 if default or id:
3862 if default or id:
3859 output = [hexoutput]
3863 output = [hexoutput]
3860 fm.data(id=hexoutput)
3864 fm.data(id=hexoutput)
3861
3865
3862 if num:
3866 if num:
3863 output.append(pycompat.bytestr(ctx.rev()))
3867 output.append(pycompat.bytestr(ctx.rev()))
3864 taglist = ctx.tags()
3868 taglist = ctx.tags()
3865
3869
3866 if default and not ui.quiet:
3870 if default and not ui.quiet:
3867 b = ctx.branch()
3871 b = ctx.branch()
3868 if b != b'default':
3872 if b != b'default':
3869 output.append(b"(%s)" % b)
3873 output.append(b"(%s)" % b)
3870
3874
3871 # multiple tags for a single parent separated by '/'
3875 # multiple tags for a single parent separated by '/'
3872 t = b'/'.join(taglist)
3876 t = b'/'.join(taglist)
3873 if t:
3877 if t:
3874 output.append(t)
3878 output.append(t)
3875
3879
3876 # multiple bookmarks for a single parent separated by '/'
3880 # multiple bookmarks for a single parent separated by '/'
3877 bm = b'/'.join(ctx.bookmarks())
3881 bm = b'/'.join(ctx.bookmarks())
3878 if bm:
3882 if bm:
3879 output.append(bm)
3883 output.append(bm)
3880 else:
3884 else:
3881 if branch:
3885 if branch:
3882 output.append(ctx.branch())
3886 output.append(ctx.branch())
3883
3887
3884 if tags:
3888 if tags:
3885 output.extend(taglist)
3889 output.extend(taglist)
3886
3890
3887 if bookmarks:
3891 if bookmarks:
3888 output.extend(ctx.bookmarks())
3892 output.extend(ctx.bookmarks())
3889
3893
3890 fm.data(node=ctx.hex())
3894 fm.data(node=ctx.hex())
3891 fm.data(branch=ctx.branch())
3895 fm.data(branch=ctx.branch())
3892 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3896 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3893 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3897 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3894 fm.context(ctx=ctx)
3898 fm.context(ctx=ctx)
3895
3899
3896 fm.plain(b"%s\n" % b' '.join(output))
3900 fm.plain(b"%s\n" % b' '.join(output))
3897 fm.end()
3901 fm.end()
3898
3902
3899
3903
3900 @command(
3904 @command(
3901 b'import|patch',
3905 b'import|patch',
3902 [
3906 [
3903 (
3907 (
3904 b'p',
3908 b'p',
3905 b'strip',
3909 b'strip',
3906 1,
3910 1,
3907 _(
3911 _(
3908 b'directory strip option for patch. This has the same '
3912 b'directory strip option for patch. This has the same '
3909 b'meaning as the corresponding patch option'
3913 b'meaning as the corresponding patch option'
3910 ),
3914 ),
3911 _(b'NUM'),
3915 _(b'NUM'),
3912 ),
3916 ),
3913 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3917 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3914 (b'', b'secret', None, _(b'use the secret phase for committing')),
3918 (b'', b'secret', None, _(b'use the secret phase for committing')),
3915 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3919 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3916 (
3920 (
3917 b'f',
3921 b'f',
3918 b'force',
3922 b'force',
3919 None,
3923 None,
3920 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3924 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3921 ),
3925 ),
3922 (
3926 (
3923 b'',
3927 b'',
3924 b'no-commit',
3928 b'no-commit',
3925 None,
3929 None,
3926 _(b"don't commit, just update the working directory"),
3930 _(b"don't commit, just update the working directory"),
3927 ),
3931 ),
3928 (
3932 (
3929 b'',
3933 b'',
3930 b'bypass',
3934 b'bypass',
3931 None,
3935 None,
3932 _(b"apply patch without touching the working directory"),
3936 _(b"apply patch without touching the working directory"),
3933 ),
3937 ),
3934 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3938 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3935 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3939 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3936 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3940 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3937 (
3941 (
3938 b'',
3942 b'',
3939 b'import-branch',
3943 b'import-branch',
3940 None,
3944 None,
3941 _(b'use any branch information in patch (implied by --exact)'),
3945 _(b'use any branch information in patch (implied by --exact)'),
3942 ),
3946 ),
3943 ]
3947 ]
3944 + commitopts
3948 + commitopts
3945 + commitopts2
3949 + commitopts2
3946 + similarityopts,
3950 + similarityopts,
3947 _(b'[OPTION]... PATCH...'),
3951 _(b'[OPTION]... PATCH...'),
3948 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3952 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3949 )
3953 )
3950 def import_(ui, repo, patch1=None, *patches, **opts):
3954 def import_(ui, repo, patch1=None, *patches, **opts):
3951 """import an ordered set of patches
3955 """import an ordered set of patches
3952
3956
3953 Import a list of patches and commit them individually (unless
3957 Import a list of patches and commit them individually (unless
3954 --no-commit is specified).
3958 --no-commit is specified).
3955
3959
3956 To read a patch from standard input (stdin), use "-" as the patch
3960 To read a patch from standard input (stdin), use "-" as the patch
3957 name. If a URL is specified, the patch will be downloaded from
3961 name. If a URL is specified, the patch will be downloaded from
3958 there.
3962 there.
3959
3963
3960 Import first applies changes to the working directory (unless
3964 Import first applies changes to the working directory (unless
3961 --bypass is specified), import will abort if there are outstanding
3965 --bypass is specified), import will abort if there are outstanding
3962 changes.
3966 changes.
3963
3967
3964 Use --bypass to apply and commit patches directly to the
3968 Use --bypass to apply and commit patches directly to the
3965 repository, without affecting the working directory. Without
3969 repository, without affecting the working directory. Without
3966 --exact, patches will be applied on top of the working directory
3970 --exact, patches will be applied on top of the working directory
3967 parent revision.
3971 parent revision.
3968
3972
3969 You can import a patch straight from a mail message. Even patches
3973 You can import a patch straight from a mail message. Even patches
3970 as attachments work (to use the body part, it must have type
3974 as attachments work (to use the body part, it must have type
3971 text/plain or text/x-patch). From and Subject headers of email
3975 text/plain or text/x-patch). From and Subject headers of email
3972 message are used as default committer and commit message. All
3976 message are used as default committer and commit message. All
3973 text/plain body parts before first diff are added to the commit
3977 text/plain body parts before first diff are added to the commit
3974 message.
3978 message.
3975
3979
3976 If the imported patch was generated by :hg:`export`, user and
3980 If the imported patch was generated by :hg:`export`, user and
3977 description from patch override values from message headers and
3981 description from patch override values from message headers and
3978 body. Values given on command line with -m/--message and -u/--user
3982 body. Values given on command line with -m/--message and -u/--user
3979 override these.
3983 override these.
3980
3984
3981 If --exact is specified, import will set the working directory to
3985 If --exact is specified, import will set the working directory to
3982 the parent of each patch before applying it, and will abort if the
3986 the parent of each patch before applying it, and will abort if the
3983 resulting changeset has a different ID than the one recorded in
3987 resulting changeset has a different ID than the one recorded in
3984 the patch. This will guard against various ways that portable
3988 the patch. This will guard against various ways that portable
3985 patch formats and mail systems might fail to transfer Mercurial
3989 patch formats and mail systems might fail to transfer Mercurial
3986 data or metadata. See :hg:`bundle` for lossless transmission.
3990 data or metadata. See :hg:`bundle` for lossless transmission.
3987
3991
3988 Use --partial to ensure a changeset will be created from the patch
3992 Use --partial to ensure a changeset will be created from the patch
3989 even if some hunks fail to apply. Hunks that fail to apply will be
3993 even if some hunks fail to apply. Hunks that fail to apply will be
3990 written to a <target-file>.rej file. Conflicts can then be resolved
3994 written to a <target-file>.rej file. Conflicts can then be resolved
3991 by hand before :hg:`commit --amend` is run to update the created
3995 by hand before :hg:`commit --amend` is run to update the created
3992 changeset. This flag exists to let people import patches that
3996 changeset. This flag exists to let people import patches that
3993 partially apply without losing the associated metadata (author,
3997 partially apply without losing the associated metadata (author,
3994 date, description, ...).
3998 date, description, ...).
3995
3999
3996 .. note::
4000 .. note::
3997
4001
3998 When no hunks apply cleanly, :hg:`import --partial` will create
4002 When no hunks apply cleanly, :hg:`import --partial` will create
3999 an empty changeset, importing only the patch metadata.
4003 an empty changeset, importing only the patch metadata.
4000
4004
4001 With -s/--similarity, hg will attempt to discover renames and
4005 With -s/--similarity, hg will attempt to discover renames and
4002 copies in the patch in the same way as :hg:`addremove`.
4006 copies in the patch in the same way as :hg:`addremove`.
4003
4007
4004 It is possible to use external patch programs to perform the patch
4008 It is possible to use external patch programs to perform the patch
4005 by setting the ``ui.patch`` configuration option. For the default
4009 by setting the ``ui.patch`` configuration option. For the default
4006 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4010 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4007 See :hg:`help config` for more information about configuration
4011 See :hg:`help config` for more information about configuration
4008 files and how to use these options.
4012 files and how to use these options.
4009
4013
4010 See :hg:`help dates` for a list of formats valid for -d/--date.
4014 See :hg:`help dates` for a list of formats valid for -d/--date.
4011
4015
4012 .. container:: verbose
4016 .. container:: verbose
4013
4017
4014 Examples:
4018 Examples:
4015
4019
4016 - import a traditional patch from a website and detect renames::
4020 - import a traditional patch from a website and detect renames::
4017
4021
4018 hg import -s 80 http://example.com/bugfix.patch
4022 hg import -s 80 http://example.com/bugfix.patch
4019
4023
4020 - import a changeset from an hgweb server::
4024 - import a changeset from an hgweb server::
4021
4025
4022 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4026 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4023
4027
4024 - import all the patches in an Unix-style mbox::
4028 - import all the patches in an Unix-style mbox::
4025
4029
4026 hg import incoming-patches.mbox
4030 hg import incoming-patches.mbox
4027
4031
4028 - import patches from stdin::
4032 - import patches from stdin::
4029
4033
4030 hg import -
4034 hg import -
4031
4035
4032 - attempt to exactly restore an exported changeset (not always
4036 - attempt to exactly restore an exported changeset (not always
4033 possible)::
4037 possible)::
4034
4038
4035 hg import --exact proposed-fix.patch
4039 hg import --exact proposed-fix.patch
4036
4040
4037 - use an external tool to apply a patch which is too fuzzy for
4041 - use an external tool to apply a patch which is too fuzzy for
4038 the default internal tool.
4042 the default internal tool.
4039
4043
4040 hg import --config ui.patch="patch --merge" fuzzy.patch
4044 hg import --config ui.patch="patch --merge" fuzzy.patch
4041
4045
4042 - change the default fuzzing from 2 to a less strict 7
4046 - change the default fuzzing from 2 to a less strict 7
4043
4047
4044 hg import --config ui.fuzz=7 fuzz.patch
4048 hg import --config ui.fuzz=7 fuzz.patch
4045
4049
4046 Returns 0 on success, 1 on partial success (see --partial).
4050 Returns 0 on success, 1 on partial success (see --partial).
4047 """
4051 """
4048
4052
4049 cmdutil.check_incompatible_arguments(
4053 cmdutil.check_incompatible_arguments(
4050 opts, 'no_commit', ['bypass', 'secret']
4054 opts, 'no_commit', ['bypass', 'secret']
4051 )
4055 )
4052 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4056 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4053 opts = pycompat.byteskwargs(opts)
4057 opts = pycompat.byteskwargs(opts)
4054 if not patch1:
4058 if not patch1:
4055 raise error.Abort(_(b'need at least one patch to import'))
4059 raise error.Abort(_(b'need at least one patch to import'))
4056
4060
4057 patches = (patch1,) + patches
4061 patches = (patch1,) + patches
4058
4062
4059 date = opts.get(b'date')
4063 date = opts.get(b'date')
4060 if date:
4064 if date:
4061 opts[b'date'] = dateutil.parsedate(date)
4065 opts[b'date'] = dateutil.parsedate(date)
4062
4066
4063 exact = opts.get(b'exact')
4067 exact = opts.get(b'exact')
4064 update = not opts.get(b'bypass')
4068 update = not opts.get(b'bypass')
4065 try:
4069 try:
4066 sim = float(opts.get(b'similarity') or 0)
4070 sim = float(opts.get(b'similarity') or 0)
4067 except ValueError:
4071 except ValueError:
4068 raise error.Abort(_(b'similarity must be a number'))
4072 raise error.Abort(_(b'similarity must be a number'))
4069 if sim < 0 or sim > 100:
4073 if sim < 0 or sim > 100:
4070 raise error.Abort(_(b'similarity must be between 0 and 100'))
4074 raise error.Abort(_(b'similarity must be between 0 and 100'))
4071 if sim and not update:
4075 if sim and not update:
4072 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4076 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4073
4077
4074 base = opts[b"base"]
4078 base = opts[b"base"]
4075 msgs = []
4079 msgs = []
4076 ret = 0
4080 ret = 0
4077
4081
4078 with repo.wlock():
4082 with repo.wlock():
4079 if update:
4083 if update:
4080 cmdutil.checkunfinished(repo)
4084 cmdutil.checkunfinished(repo)
4081 if exact or not opts.get(b'force'):
4085 if exact or not opts.get(b'force'):
4082 cmdutil.bailifchanged(repo)
4086 cmdutil.bailifchanged(repo)
4083
4087
4084 if not opts.get(b'no_commit'):
4088 if not opts.get(b'no_commit'):
4085 lock = repo.lock
4089 lock = repo.lock
4086 tr = lambda: repo.transaction(b'import')
4090 tr = lambda: repo.transaction(b'import')
4087 dsguard = util.nullcontextmanager
4091 dsguard = util.nullcontextmanager
4088 else:
4092 else:
4089 lock = util.nullcontextmanager
4093 lock = util.nullcontextmanager
4090 tr = util.nullcontextmanager
4094 tr = util.nullcontextmanager
4091 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4095 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4092 with lock(), tr(), dsguard():
4096 with lock(), tr(), dsguard():
4093 parents = repo[None].parents()
4097 parents = repo[None].parents()
4094 for patchurl in patches:
4098 for patchurl in patches:
4095 if patchurl == b'-':
4099 if patchurl == b'-':
4096 ui.status(_(b'applying patch from stdin\n'))
4100 ui.status(_(b'applying patch from stdin\n'))
4097 patchfile = ui.fin
4101 patchfile = ui.fin
4098 patchurl = b'stdin' # for error message
4102 patchurl = b'stdin' # for error message
4099 else:
4103 else:
4100 patchurl = os.path.join(base, patchurl)
4104 patchurl = os.path.join(base, patchurl)
4101 ui.status(_(b'applying %s\n') % patchurl)
4105 ui.status(_(b'applying %s\n') % patchurl)
4102 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4106 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4103
4107
4104 haspatch = False
4108 haspatch = False
4105 for hunk in patch.split(patchfile):
4109 for hunk in patch.split(patchfile):
4106 with patch.extract(ui, hunk) as patchdata:
4110 with patch.extract(ui, hunk) as patchdata:
4107 msg, node, rej = cmdutil.tryimportone(
4111 msg, node, rej = cmdutil.tryimportone(
4108 ui, repo, patchdata, parents, opts, msgs, hg.clean
4112 ui, repo, patchdata, parents, opts, msgs, hg.clean
4109 )
4113 )
4110 if msg:
4114 if msg:
4111 haspatch = True
4115 haspatch = True
4112 ui.note(msg + b'\n')
4116 ui.note(msg + b'\n')
4113 if update or exact:
4117 if update or exact:
4114 parents = repo[None].parents()
4118 parents = repo[None].parents()
4115 else:
4119 else:
4116 parents = [repo[node]]
4120 parents = [repo[node]]
4117 if rej:
4121 if rej:
4118 ui.write_err(_(b"patch applied partially\n"))
4122 ui.write_err(_(b"patch applied partially\n"))
4119 ui.write_err(
4123 ui.write_err(
4120 _(
4124 _(
4121 b"(fix the .rej files and run "
4125 b"(fix the .rej files and run "
4122 b"`hg commit --amend`)\n"
4126 b"`hg commit --amend`)\n"
4123 )
4127 )
4124 )
4128 )
4125 ret = 1
4129 ret = 1
4126 break
4130 break
4127
4131
4128 if not haspatch:
4132 if not haspatch:
4129 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4133 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4130
4134
4131 if msgs:
4135 if msgs:
4132 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4136 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4133 return ret
4137 return ret
4134
4138
4135
4139
4136 @command(
4140 @command(
4137 b'incoming|in',
4141 b'incoming|in',
4138 [
4142 [
4139 (
4143 (
4140 b'f',
4144 b'f',
4141 b'force',
4145 b'force',
4142 None,
4146 None,
4143 _(b'run even if remote repository is unrelated'),
4147 _(b'run even if remote repository is unrelated'),
4144 ),
4148 ),
4145 (b'n', b'newest-first', None, _(b'show newest record first')),
4149 (b'n', b'newest-first', None, _(b'show newest record first')),
4146 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4150 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4147 (
4151 (
4148 b'r',
4152 b'r',
4149 b'rev',
4153 b'rev',
4150 [],
4154 [],
4151 _(b'a remote changeset intended to be added'),
4155 _(b'a remote changeset intended to be added'),
4152 _(b'REV'),
4156 _(b'REV'),
4153 ),
4157 ),
4154 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4158 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4155 (
4159 (
4156 b'b',
4160 b'b',
4157 b'branch',
4161 b'branch',
4158 [],
4162 [],
4159 _(b'a specific branch you would like to pull'),
4163 _(b'a specific branch you would like to pull'),
4160 _(b'BRANCH'),
4164 _(b'BRANCH'),
4161 ),
4165 ),
4162 ]
4166 ]
4163 + logopts
4167 + logopts
4164 + remoteopts
4168 + remoteopts
4165 + subrepoopts,
4169 + subrepoopts,
4166 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4170 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4167 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4171 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4168 )
4172 )
4169 def incoming(ui, repo, source=b"default", **opts):
4173 def incoming(ui, repo, source=b"default", **opts):
4170 """show new changesets found in source
4174 """show new changesets found in source
4171
4175
4172 Show new changesets found in the specified path/URL or the default
4176 Show new changesets found in the specified path/URL or the default
4173 pull location. These are the changesets that would have been pulled
4177 pull location. These are the changesets that would have been pulled
4174 by :hg:`pull` at the time you issued this command.
4178 by :hg:`pull` at the time you issued this command.
4175
4179
4176 See pull for valid source format details.
4180 See pull for valid source format details.
4177
4181
4178 .. container:: verbose
4182 .. container:: verbose
4179
4183
4180 With -B/--bookmarks, the result of bookmark comparison between
4184 With -B/--bookmarks, the result of bookmark comparison between
4181 local and remote repositories is displayed. With -v/--verbose,
4185 local and remote repositories is displayed. With -v/--verbose,
4182 status is also displayed for each bookmark like below::
4186 status is also displayed for each bookmark like below::
4183
4187
4184 BM1 01234567890a added
4188 BM1 01234567890a added
4185 BM2 1234567890ab advanced
4189 BM2 1234567890ab advanced
4186 BM3 234567890abc diverged
4190 BM3 234567890abc diverged
4187 BM4 34567890abcd changed
4191 BM4 34567890abcd changed
4188
4192
4189 The action taken locally when pulling depends on the
4193 The action taken locally when pulling depends on the
4190 status of each bookmark:
4194 status of each bookmark:
4191
4195
4192 :``added``: pull will create it
4196 :``added``: pull will create it
4193 :``advanced``: pull will update it
4197 :``advanced``: pull will update it
4194 :``diverged``: pull will create a divergent bookmark
4198 :``diverged``: pull will create a divergent bookmark
4195 :``changed``: result depends on remote changesets
4199 :``changed``: result depends on remote changesets
4196
4200
4197 From the point of view of pulling behavior, bookmark
4201 From the point of view of pulling behavior, bookmark
4198 existing only in the remote repository are treated as ``added``,
4202 existing only in the remote repository are treated as ``added``,
4199 even if it is in fact locally deleted.
4203 even if it is in fact locally deleted.
4200
4204
4201 .. container:: verbose
4205 .. container:: verbose
4202
4206
4203 For remote repository, using --bundle avoids downloading the
4207 For remote repository, using --bundle avoids downloading the
4204 changesets twice if the incoming is followed by a pull.
4208 changesets twice if the incoming is followed by a pull.
4205
4209
4206 Examples:
4210 Examples:
4207
4211
4208 - show incoming changes with patches and full description::
4212 - show incoming changes with patches and full description::
4209
4213
4210 hg incoming -vp
4214 hg incoming -vp
4211
4215
4212 - show incoming changes excluding merges, store a bundle::
4216 - show incoming changes excluding merges, store a bundle::
4213
4217
4214 hg in -vpM --bundle incoming.hg
4218 hg in -vpM --bundle incoming.hg
4215 hg pull incoming.hg
4219 hg pull incoming.hg
4216
4220
4217 - briefly list changes inside a bundle::
4221 - briefly list changes inside a bundle::
4218
4222
4219 hg in changes.hg -T "{desc|firstline}\\n"
4223 hg in changes.hg -T "{desc|firstline}\\n"
4220
4224
4221 Returns 0 if there are incoming changes, 1 otherwise.
4225 Returns 0 if there are incoming changes, 1 otherwise.
4222 """
4226 """
4223 opts = pycompat.byteskwargs(opts)
4227 opts = pycompat.byteskwargs(opts)
4224 if opts.get(b'graph'):
4228 if opts.get(b'graph'):
4225 logcmdutil.checkunsupportedgraphflags([], opts)
4229 logcmdutil.checkunsupportedgraphflags([], opts)
4226
4230
4227 def display(other, chlist, displayer):
4231 def display(other, chlist, displayer):
4228 revdag = logcmdutil.graphrevs(other, chlist, opts)
4232 revdag = logcmdutil.graphrevs(other, chlist, opts)
4229 logcmdutil.displaygraph(
4233 logcmdutil.displaygraph(
4230 ui, repo, revdag, displayer, graphmod.asciiedges
4234 ui, repo, revdag, displayer, graphmod.asciiedges
4231 )
4235 )
4232
4236
4233 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4237 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4234 return 0
4238 return 0
4235
4239
4236 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4240 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4237
4241
4238 if opts.get(b'bookmarks'):
4242 if opts.get(b'bookmarks'):
4239 source, branches = hg.parseurl(
4243 source, branches = hg.parseurl(
4240 ui.expandpath(source), opts.get(b'branch')
4244 ui.expandpath(source), opts.get(b'branch')
4241 )
4245 )
4242 other = hg.peer(repo, opts, source)
4246 other = hg.peer(repo, opts, source)
4243 if b'bookmarks' not in other.listkeys(b'namespaces'):
4247 if b'bookmarks' not in other.listkeys(b'namespaces'):
4244 ui.warn(_(b"remote doesn't support bookmarks\n"))
4248 ui.warn(_(b"remote doesn't support bookmarks\n"))
4245 return 0
4249 return 0
4246 ui.pager(b'incoming')
4250 ui.pager(b'incoming')
4247 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4251 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4248 return bookmarks.incoming(ui, repo, other)
4252 return bookmarks.incoming(ui, repo, other)
4249
4253
4250 repo._subtoppath = ui.expandpath(source)
4254 repo._subtoppath = ui.expandpath(source)
4251 try:
4255 try:
4252 return hg.incoming(ui, repo, source, opts)
4256 return hg.incoming(ui, repo, source, opts)
4253 finally:
4257 finally:
4254 del repo._subtoppath
4258 del repo._subtoppath
4255
4259
4256
4260
4257 @command(
4261 @command(
4258 b'init',
4262 b'init',
4259 remoteopts,
4263 remoteopts,
4260 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4264 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4261 helpcategory=command.CATEGORY_REPO_CREATION,
4265 helpcategory=command.CATEGORY_REPO_CREATION,
4262 helpbasic=True,
4266 helpbasic=True,
4263 norepo=True,
4267 norepo=True,
4264 )
4268 )
4265 def init(ui, dest=b".", **opts):
4269 def init(ui, dest=b".", **opts):
4266 """create a new repository in the given directory
4270 """create a new repository in the given directory
4267
4271
4268 Initialize a new repository in the given directory. If the given
4272 Initialize a new repository in the given directory. If the given
4269 directory does not exist, it will be created.
4273 directory does not exist, it will be created.
4270
4274
4271 If no directory is given, the current directory is used.
4275 If no directory is given, the current directory is used.
4272
4276
4273 It is possible to specify an ``ssh://`` URL as the destination.
4277 It is possible to specify an ``ssh://`` URL as the destination.
4274 See :hg:`help urls` for more information.
4278 See :hg:`help urls` for more information.
4275
4279
4276 Returns 0 on success.
4280 Returns 0 on success.
4277 """
4281 """
4278 opts = pycompat.byteskwargs(opts)
4282 opts = pycompat.byteskwargs(opts)
4279 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4283 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4280
4284
4281
4285
4282 @command(
4286 @command(
4283 b'locate',
4287 b'locate',
4284 [
4288 [
4285 (
4289 (
4286 b'r',
4290 b'r',
4287 b'rev',
4291 b'rev',
4288 b'',
4292 b'',
4289 _(b'search the repository as it is in REV'),
4293 _(b'search the repository as it is in REV'),
4290 _(b'REV'),
4294 _(b'REV'),
4291 ),
4295 ),
4292 (
4296 (
4293 b'0',
4297 b'0',
4294 b'print0',
4298 b'print0',
4295 None,
4299 None,
4296 _(b'end filenames with NUL, for use with xargs'),
4300 _(b'end filenames with NUL, for use with xargs'),
4297 ),
4301 ),
4298 (
4302 (
4299 b'f',
4303 b'f',
4300 b'fullpath',
4304 b'fullpath',
4301 None,
4305 None,
4302 _(b'print complete paths from the filesystem root'),
4306 _(b'print complete paths from the filesystem root'),
4303 ),
4307 ),
4304 ]
4308 ]
4305 + walkopts,
4309 + walkopts,
4306 _(b'[OPTION]... [PATTERN]...'),
4310 _(b'[OPTION]... [PATTERN]...'),
4307 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4311 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4308 )
4312 )
4309 def locate(ui, repo, *pats, **opts):
4313 def locate(ui, repo, *pats, **opts):
4310 """locate files matching specific patterns (DEPRECATED)
4314 """locate files matching specific patterns (DEPRECATED)
4311
4315
4312 Print files under Mercurial control in the working directory whose
4316 Print files under Mercurial control in the working directory whose
4313 names match the given patterns.
4317 names match the given patterns.
4314
4318
4315 By default, this command searches all directories in the working
4319 By default, this command searches all directories in the working
4316 directory. To search just the current directory and its
4320 directory. To search just the current directory and its
4317 subdirectories, use "--include .".
4321 subdirectories, use "--include .".
4318
4322
4319 If no patterns are given to match, this command prints the names
4323 If no patterns are given to match, this command prints the names
4320 of all files under Mercurial control in the working directory.
4324 of all files under Mercurial control in the working directory.
4321
4325
4322 If you want to feed the output of this command into the "xargs"
4326 If you want to feed the output of this command into the "xargs"
4323 command, use the -0 option to both this command and "xargs". This
4327 command, use the -0 option to both this command and "xargs". This
4324 will avoid the problem of "xargs" treating single filenames that
4328 will avoid the problem of "xargs" treating single filenames that
4325 contain whitespace as multiple filenames.
4329 contain whitespace as multiple filenames.
4326
4330
4327 See :hg:`help files` for a more versatile command.
4331 See :hg:`help files` for a more versatile command.
4328
4332
4329 Returns 0 if a match is found, 1 otherwise.
4333 Returns 0 if a match is found, 1 otherwise.
4330 """
4334 """
4331 opts = pycompat.byteskwargs(opts)
4335 opts = pycompat.byteskwargs(opts)
4332 if opts.get(b'print0'):
4336 if opts.get(b'print0'):
4333 end = b'\0'
4337 end = b'\0'
4334 else:
4338 else:
4335 end = b'\n'
4339 end = b'\n'
4336 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4340 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4337
4341
4338 ret = 1
4342 ret = 1
4339 m = scmutil.match(
4343 m = scmutil.match(
4340 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4344 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4341 )
4345 )
4342
4346
4343 ui.pager(b'locate')
4347 ui.pager(b'locate')
4344 if ctx.rev() is None:
4348 if ctx.rev() is None:
4345 # When run on the working copy, "locate" includes removed files, so
4349 # When run on the working copy, "locate" includes removed files, so
4346 # we get the list of files from the dirstate.
4350 # we get the list of files from the dirstate.
4347 filesgen = sorted(repo.dirstate.matches(m))
4351 filesgen = sorted(repo.dirstate.matches(m))
4348 else:
4352 else:
4349 filesgen = ctx.matches(m)
4353 filesgen = ctx.matches(m)
4350 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4354 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4351 for abs in filesgen:
4355 for abs in filesgen:
4352 if opts.get(b'fullpath'):
4356 if opts.get(b'fullpath'):
4353 ui.write(repo.wjoin(abs), end)
4357 ui.write(repo.wjoin(abs), end)
4354 else:
4358 else:
4355 ui.write(uipathfn(abs), end)
4359 ui.write(uipathfn(abs), end)
4356 ret = 0
4360 ret = 0
4357
4361
4358 return ret
4362 return ret
4359
4363
4360
4364
4361 @command(
4365 @command(
4362 b'log|history',
4366 b'log|history',
4363 [
4367 [
4364 (
4368 (
4365 b'f',
4369 b'f',
4366 b'follow',
4370 b'follow',
4367 None,
4371 None,
4368 _(
4372 _(
4369 b'follow changeset history, or file history across copies and renames'
4373 b'follow changeset history, or file history across copies and renames'
4370 ),
4374 ),
4371 ),
4375 ),
4372 (
4376 (
4373 b'',
4377 b'',
4374 b'follow-first',
4378 b'follow-first',
4375 None,
4379 None,
4376 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4380 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4377 ),
4381 ),
4378 (
4382 (
4379 b'd',
4383 b'd',
4380 b'date',
4384 b'date',
4381 b'',
4385 b'',
4382 _(b'show revisions matching date spec'),
4386 _(b'show revisions matching date spec'),
4383 _(b'DATE'),
4387 _(b'DATE'),
4384 ),
4388 ),
4385 (b'C', b'copies', None, _(b'show copied files')),
4389 (b'C', b'copies', None, _(b'show copied files')),
4386 (
4390 (
4387 b'k',
4391 b'k',
4388 b'keyword',
4392 b'keyword',
4389 [],
4393 [],
4390 _(b'do case-insensitive search for a given text'),
4394 _(b'do case-insensitive search for a given text'),
4391 _(b'TEXT'),
4395 _(b'TEXT'),
4392 ),
4396 ),
4393 (
4397 (
4394 b'r',
4398 b'r',
4395 b'rev',
4399 b'rev',
4396 [],
4400 [],
4397 _(b'show the specified revision or revset'),
4401 _(b'show the specified revision or revset'),
4398 _(b'REV'),
4402 _(b'REV'),
4399 ),
4403 ),
4400 (
4404 (
4401 b'L',
4405 b'L',
4402 b'line-range',
4406 b'line-range',
4403 [],
4407 [],
4404 _(b'follow line range of specified file (EXPERIMENTAL)'),
4408 _(b'follow line range of specified file (EXPERIMENTAL)'),
4405 _(b'FILE,RANGE'),
4409 _(b'FILE,RANGE'),
4406 ),
4410 ),
4407 (
4411 (
4408 b'',
4412 b'',
4409 b'removed',
4413 b'removed',
4410 None,
4414 None,
4411 _(b'include revisions where files were removed'),
4415 _(b'include revisions where files were removed'),
4412 ),
4416 ),
4413 (
4417 (
4414 b'm',
4418 b'm',
4415 b'only-merges',
4419 b'only-merges',
4416 None,
4420 None,
4417 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4421 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4418 ),
4422 ),
4419 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4423 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4420 (
4424 (
4421 b'',
4425 b'',
4422 b'only-branch',
4426 b'only-branch',
4423 [],
4427 [],
4424 _(
4428 _(
4425 b'show only changesets within the given named branch (DEPRECATED)'
4429 b'show only changesets within the given named branch (DEPRECATED)'
4426 ),
4430 ),
4427 _(b'BRANCH'),
4431 _(b'BRANCH'),
4428 ),
4432 ),
4429 (
4433 (
4430 b'b',
4434 b'b',
4431 b'branch',
4435 b'branch',
4432 [],
4436 [],
4433 _(b'show changesets within the given named branch'),
4437 _(b'show changesets within the given named branch'),
4434 _(b'BRANCH'),
4438 _(b'BRANCH'),
4435 ),
4439 ),
4436 (
4440 (
4437 b'P',
4441 b'P',
4438 b'prune',
4442 b'prune',
4439 [],
4443 [],
4440 _(b'do not display revision or any of its ancestors'),
4444 _(b'do not display revision or any of its ancestors'),
4441 _(b'REV'),
4445 _(b'REV'),
4442 ),
4446 ),
4443 ]
4447 ]
4444 + logopts
4448 + logopts
4445 + walkopts,
4449 + walkopts,
4446 _(b'[OPTION]... [FILE]'),
4450 _(b'[OPTION]... [FILE]'),
4447 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4451 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4448 helpbasic=True,
4452 helpbasic=True,
4449 inferrepo=True,
4453 inferrepo=True,
4450 intents={INTENT_READONLY},
4454 intents={INTENT_READONLY},
4451 )
4455 )
4452 def log(ui, repo, *pats, **opts):
4456 def log(ui, repo, *pats, **opts):
4453 """show revision history of entire repository or files
4457 """show revision history of entire repository or files
4454
4458
4455 Print the revision history of the specified files or the entire
4459 Print the revision history of the specified files or the entire
4456 project.
4460 project.
4457
4461
4458 If no revision range is specified, the default is ``tip:0`` unless
4462 If no revision range is specified, the default is ``tip:0`` unless
4459 --follow is set, in which case the working directory parent is
4463 --follow is set, in which case the working directory parent is
4460 used as the starting revision.
4464 used as the starting revision.
4461
4465
4462 File history is shown without following rename or copy history of
4466 File history is shown without following rename or copy history of
4463 files. Use -f/--follow with a filename to follow history across
4467 files. Use -f/--follow with a filename to follow history across
4464 renames and copies. --follow without a filename will only show
4468 renames and copies. --follow without a filename will only show
4465 ancestors of the starting revision.
4469 ancestors of the starting revision.
4466
4470
4467 By default this command prints revision number and changeset id,
4471 By default this command prints revision number and changeset id,
4468 tags, non-trivial parents, user, date and time, and a summary for
4472 tags, non-trivial parents, user, date and time, and a summary for
4469 each commit. When the -v/--verbose switch is used, the list of
4473 each commit. When the -v/--verbose switch is used, the list of
4470 changed files and full commit message are shown.
4474 changed files and full commit message are shown.
4471
4475
4472 With --graph the revisions are shown as an ASCII art DAG with the most
4476 With --graph the revisions are shown as an ASCII art DAG with the most
4473 recent changeset at the top.
4477 recent changeset at the top.
4474 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4478 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4475 involved in an unresolved merge conflict, '_' closes a branch,
4479 involved in an unresolved merge conflict, '_' closes a branch,
4476 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4480 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4477 changeset from the lines below is a parent of the 'o' merge on the same
4481 changeset from the lines below is a parent of the 'o' merge on the same
4478 line.
4482 line.
4479 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4483 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4480 of a '|' indicates one or more revisions in a path are omitted.
4484 of a '|' indicates one or more revisions in a path are omitted.
4481
4485
4482 .. container:: verbose
4486 .. container:: verbose
4483
4487
4484 Use -L/--line-range FILE,M:N options to follow the history of lines
4488 Use -L/--line-range FILE,M:N options to follow the history of lines
4485 from M to N in FILE. With -p/--patch only diff hunks affecting
4489 from M to N in FILE. With -p/--patch only diff hunks affecting
4486 specified line range will be shown. This option requires --follow;
4490 specified line range will be shown. This option requires --follow;
4487 it can be specified multiple times. Currently, this option is not
4491 it can be specified multiple times. Currently, this option is not
4488 compatible with --graph. This option is experimental.
4492 compatible with --graph. This option is experimental.
4489
4493
4490 .. note::
4494 .. note::
4491
4495
4492 :hg:`log --patch` may generate unexpected diff output for merge
4496 :hg:`log --patch` may generate unexpected diff output for merge
4493 changesets, as it will only compare the merge changeset against
4497 changesets, as it will only compare the merge changeset against
4494 its first parent. Also, only files different from BOTH parents
4498 its first parent. Also, only files different from BOTH parents
4495 will appear in files:.
4499 will appear in files:.
4496
4500
4497 .. note::
4501 .. note::
4498
4502
4499 For performance reasons, :hg:`log FILE` may omit duplicate changes
4503 For performance reasons, :hg:`log FILE` may omit duplicate changes
4500 made on branches and will not show removals or mode changes. To
4504 made on branches and will not show removals or mode changes. To
4501 see all such changes, use the --removed switch.
4505 see all such changes, use the --removed switch.
4502
4506
4503 .. container:: verbose
4507 .. container:: verbose
4504
4508
4505 .. note::
4509 .. note::
4506
4510
4507 The history resulting from -L/--line-range options depends on diff
4511 The history resulting from -L/--line-range options depends on diff
4508 options; for instance if white-spaces are ignored, respective changes
4512 options; for instance if white-spaces are ignored, respective changes
4509 with only white-spaces in specified line range will not be listed.
4513 with only white-spaces in specified line range will not be listed.
4510
4514
4511 .. container:: verbose
4515 .. container:: verbose
4512
4516
4513 Some examples:
4517 Some examples:
4514
4518
4515 - changesets with full descriptions and file lists::
4519 - changesets with full descriptions and file lists::
4516
4520
4517 hg log -v
4521 hg log -v
4518
4522
4519 - changesets ancestral to the working directory::
4523 - changesets ancestral to the working directory::
4520
4524
4521 hg log -f
4525 hg log -f
4522
4526
4523 - last 10 commits on the current branch::
4527 - last 10 commits on the current branch::
4524
4528
4525 hg log -l 10 -b .
4529 hg log -l 10 -b .
4526
4530
4527 - changesets showing all modifications of a file, including removals::
4531 - changesets showing all modifications of a file, including removals::
4528
4532
4529 hg log --removed file.c
4533 hg log --removed file.c
4530
4534
4531 - all changesets that touch a directory, with diffs, excluding merges::
4535 - all changesets that touch a directory, with diffs, excluding merges::
4532
4536
4533 hg log -Mp lib/
4537 hg log -Mp lib/
4534
4538
4535 - all revision numbers that match a keyword::
4539 - all revision numbers that match a keyword::
4536
4540
4537 hg log -k bug --template "{rev}\\n"
4541 hg log -k bug --template "{rev}\\n"
4538
4542
4539 - the full hash identifier of the working directory parent::
4543 - the full hash identifier of the working directory parent::
4540
4544
4541 hg log -r . --template "{node}\\n"
4545 hg log -r . --template "{node}\\n"
4542
4546
4543 - list available log templates::
4547 - list available log templates::
4544
4548
4545 hg log -T list
4549 hg log -T list
4546
4550
4547 - check if a given changeset is included in a tagged release::
4551 - check if a given changeset is included in a tagged release::
4548
4552
4549 hg log -r "a21ccf and ancestor(1.9)"
4553 hg log -r "a21ccf and ancestor(1.9)"
4550
4554
4551 - find all changesets by some user in a date range::
4555 - find all changesets by some user in a date range::
4552
4556
4553 hg log -k alice -d "may 2008 to jul 2008"
4557 hg log -k alice -d "may 2008 to jul 2008"
4554
4558
4555 - summary of all changesets after the last tag::
4559 - summary of all changesets after the last tag::
4556
4560
4557 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4561 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4558
4562
4559 - changesets touching lines 13 to 23 for file.c::
4563 - changesets touching lines 13 to 23 for file.c::
4560
4564
4561 hg log -L file.c,13:23
4565 hg log -L file.c,13:23
4562
4566
4563 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4567 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4564 main.c with patch::
4568 main.c with patch::
4565
4569
4566 hg log -L file.c,13:23 -L main.c,2:6 -p
4570 hg log -L file.c,13:23 -L main.c,2:6 -p
4567
4571
4568 See :hg:`help dates` for a list of formats valid for -d/--date.
4572 See :hg:`help dates` for a list of formats valid for -d/--date.
4569
4573
4570 See :hg:`help revisions` for more about specifying and ordering
4574 See :hg:`help revisions` for more about specifying and ordering
4571 revisions.
4575 revisions.
4572
4576
4573 See :hg:`help templates` for more about pre-packaged styles and
4577 See :hg:`help templates` for more about pre-packaged styles and
4574 specifying custom templates. The default template used by the log
4578 specifying custom templates. The default template used by the log
4575 command can be customized via the ``command-templates.log`` configuration
4579 command can be customized via the ``command-templates.log`` configuration
4576 setting.
4580 setting.
4577
4581
4578 Returns 0 on success.
4582 Returns 0 on success.
4579
4583
4580 """
4584 """
4581 opts = pycompat.byteskwargs(opts)
4585 opts = pycompat.byteskwargs(opts)
4582 linerange = opts.get(b'line_range')
4586 linerange = opts.get(b'line_range')
4583
4587
4584 if linerange and not opts.get(b'follow'):
4588 if linerange and not opts.get(b'follow'):
4585 raise error.Abort(_(b'--line-range requires --follow'))
4589 raise error.Abort(_(b'--line-range requires --follow'))
4586
4590
4587 if linerange and pats:
4591 if linerange and pats:
4588 # TODO: take pats as patterns with no line-range filter
4592 # TODO: take pats as patterns with no line-range filter
4589 raise error.Abort(
4593 raise error.Abort(
4590 _(b'FILE arguments are not compatible with --line-range option')
4594 _(b'FILE arguments are not compatible with --line-range option')
4591 )
4595 )
4592
4596
4593 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4597 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4594 revs, differ = logcmdutil.getrevs(
4598 revs, differ = logcmdutil.getrevs(
4595 repo, logcmdutil.parseopts(ui, pats, opts)
4599 repo, logcmdutil.parseopts(ui, pats, opts)
4596 )
4600 )
4597 if linerange:
4601 if linerange:
4598 # TODO: should follow file history from logcmdutil._initialrevs(),
4602 # TODO: should follow file history from logcmdutil._initialrevs(),
4599 # then filter the result by logcmdutil._makerevset() and --limit
4603 # then filter the result by logcmdutil._makerevset() and --limit
4600 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4604 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4601
4605
4602 getcopies = None
4606 getcopies = None
4603 if opts.get(b'copies'):
4607 if opts.get(b'copies'):
4604 endrev = None
4608 endrev = None
4605 if revs:
4609 if revs:
4606 endrev = revs.max() + 1
4610 endrev = revs.max() + 1
4607 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4611 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4608
4612
4609 ui.pager(b'log')
4613 ui.pager(b'log')
4610 displayer = logcmdutil.changesetdisplayer(
4614 displayer = logcmdutil.changesetdisplayer(
4611 ui, repo, opts, differ, buffered=True
4615 ui, repo, opts, differ, buffered=True
4612 )
4616 )
4613 if opts.get(b'graph'):
4617 if opts.get(b'graph'):
4614 displayfn = logcmdutil.displaygraphrevs
4618 displayfn = logcmdutil.displaygraphrevs
4615 else:
4619 else:
4616 displayfn = logcmdutil.displayrevs
4620 displayfn = logcmdutil.displayrevs
4617 displayfn(ui, repo, revs, displayer, getcopies)
4621 displayfn(ui, repo, revs, displayer, getcopies)
4618
4622
4619
4623
4620 @command(
4624 @command(
4621 b'manifest',
4625 b'manifest',
4622 [
4626 [
4623 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4627 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4624 (b'', b'all', False, _(b"list files from all revisions")),
4628 (b'', b'all', False, _(b"list files from all revisions")),
4625 ]
4629 ]
4626 + formatteropts,
4630 + formatteropts,
4627 _(b'[-r REV]'),
4631 _(b'[-r REV]'),
4628 helpcategory=command.CATEGORY_MAINTENANCE,
4632 helpcategory=command.CATEGORY_MAINTENANCE,
4629 intents={INTENT_READONLY},
4633 intents={INTENT_READONLY},
4630 )
4634 )
4631 def manifest(ui, repo, node=None, rev=None, **opts):
4635 def manifest(ui, repo, node=None, rev=None, **opts):
4632 """output the current or given revision of the project manifest
4636 """output the current or given revision of the project manifest
4633
4637
4634 Print a list of version controlled files for the given revision.
4638 Print a list of version controlled files for the given revision.
4635 If no revision is given, the first parent of the working directory
4639 If no revision is given, the first parent of the working directory
4636 is used, or the null revision if no revision is checked out.
4640 is used, or the null revision if no revision is checked out.
4637
4641
4638 With -v, print file permissions, symlink and executable bits.
4642 With -v, print file permissions, symlink and executable bits.
4639 With --debug, print file revision hashes.
4643 With --debug, print file revision hashes.
4640
4644
4641 If option --all is specified, the list of all files from all revisions
4645 If option --all is specified, the list of all files from all revisions
4642 is printed. This includes deleted and renamed files.
4646 is printed. This includes deleted and renamed files.
4643
4647
4644 Returns 0 on success.
4648 Returns 0 on success.
4645 """
4649 """
4646 opts = pycompat.byteskwargs(opts)
4650 opts = pycompat.byteskwargs(opts)
4647 fm = ui.formatter(b'manifest', opts)
4651 fm = ui.formatter(b'manifest', opts)
4648
4652
4649 if opts.get(b'all'):
4653 if opts.get(b'all'):
4650 if rev or node:
4654 if rev or node:
4651 raise error.Abort(_(b"can't specify a revision with --all"))
4655 raise error.Abort(_(b"can't specify a revision with --all"))
4652
4656
4653 res = set()
4657 res = set()
4654 for rev in repo:
4658 for rev in repo:
4655 ctx = repo[rev]
4659 ctx = repo[rev]
4656 res |= set(ctx.files())
4660 res |= set(ctx.files())
4657
4661
4658 ui.pager(b'manifest')
4662 ui.pager(b'manifest')
4659 for f in sorted(res):
4663 for f in sorted(res):
4660 fm.startitem()
4664 fm.startitem()
4661 fm.write(b"path", b'%s\n', f)
4665 fm.write(b"path", b'%s\n', f)
4662 fm.end()
4666 fm.end()
4663 return
4667 return
4664
4668
4665 if rev and node:
4669 if rev and node:
4666 raise error.Abort(_(b"please specify just one revision"))
4670 raise error.Abort(_(b"please specify just one revision"))
4667
4671
4668 if not node:
4672 if not node:
4669 node = rev
4673 node = rev
4670
4674
4671 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4675 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4672 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4676 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4673 if node:
4677 if node:
4674 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4678 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4675 ctx = scmutil.revsingle(repo, node)
4679 ctx = scmutil.revsingle(repo, node)
4676 mf = ctx.manifest()
4680 mf = ctx.manifest()
4677 ui.pager(b'manifest')
4681 ui.pager(b'manifest')
4678 for f in ctx:
4682 for f in ctx:
4679 fm.startitem()
4683 fm.startitem()
4680 fm.context(ctx=ctx)
4684 fm.context(ctx=ctx)
4681 fl = ctx[f].flags()
4685 fl = ctx[f].flags()
4682 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4686 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4683 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4687 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4684 fm.write(b'path', b'%s\n', f)
4688 fm.write(b'path', b'%s\n', f)
4685 fm.end()
4689 fm.end()
4686
4690
4687
4691
4688 @command(
4692 @command(
4689 b'merge',
4693 b'merge',
4690 [
4694 [
4691 (
4695 (
4692 b'f',
4696 b'f',
4693 b'force',
4697 b'force',
4694 None,
4698 None,
4695 _(b'force a merge including outstanding changes (DEPRECATED)'),
4699 _(b'force a merge including outstanding changes (DEPRECATED)'),
4696 ),
4700 ),
4697 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4701 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4698 (
4702 (
4699 b'P',
4703 b'P',
4700 b'preview',
4704 b'preview',
4701 None,
4705 None,
4702 _(b'review revisions to merge (no merge is performed)'),
4706 _(b'review revisions to merge (no merge is performed)'),
4703 ),
4707 ),
4704 (b'', b'abort', None, _(b'abort the ongoing merge')),
4708 (b'', b'abort', None, _(b'abort the ongoing merge')),
4705 ]
4709 ]
4706 + mergetoolopts,
4710 + mergetoolopts,
4707 _(b'[-P] [[-r] REV]'),
4711 _(b'[-P] [[-r] REV]'),
4708 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4712 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4709 helpbasic=True,
4713 helpbasic=True,
4710 )
4714 )
4711 def merge(ui, repo, node=None, **opts):
4715 def merge(ui, repo, node=None, **opts):
4712 """merge another revision into working directory
4716 """merge another revision into working directory
4713
4717
4714 The current working directory is updated with all changes made in
4718 The current working directory is updated with all changes made in
4715 the requested revision since the last common predecessor revision.
4719 the requested revision since the last common predecessor revision.
4716
4720
4717 Files that changed between either parent are marked as changed for
4721 Files that changed between either parent are marked as changed for
4718 the next commit and a commit must be performed before any further
4722 the next commit and a commit must be performed before any further
4719 updates to the repository are allowed. The next commit will have
4723 updates to the repository are allowed. The next commit will have
4720 two parents.
4724 two parents.
4721
4725
4722 ``--tool`` can be used to specify the merge tool used for file
4726 ``--tool`` can be used to specify the merge tool used for file
4723 merges. It overrides the HGMERGE environment variable and your
4727 merges. It overrides the HGMERGE environment variable and your
4724 configuration files. See :hg:`help merge-tools` for options.
4728 configuration files. See :hg:`help merge-tools` for options.
4725
4729
4726 If no revision is specified, the working directory's parent is a
4730 If no revision is specified, the working directory's parent is a
4727 head revision, and the current branch contains exactly one other
4731 head revision, and the current branch contains exactly one other
4728 head, the other head is merged with by default. Otherwise, an
4732 head, the other head is merged with by default. Otherwise, an
4729 explicit revision with which to merge must be provided.
4733 explicit revision with which to merge must be provided.
4730
4734
4731 See :hg:`help resolve` for information on handling file conflicts.
4735 See :hg:`help resolve` for information on handling file conflicts.
4732
4736
4733 To undo an uncommitted merge, use :hg:`merge --abort` which
4737 To undo an uncommitted merge, use :hg:`merge --abort` which
4734 will check out a clean copy of the original merge parent, losing
4738 will check out a clean copy of the original merge parent, losing
4735 all changes.
4739 all changes.
4736
4740
4737 Returns 0 on success, 1 if there are unresolved files.
4741 Returns 0 on success, 1 if there are unresolved files.
4738 """
4742 """
4739
4743
4740 opts = pycompat.byteskwargs(opts)
4744 opts = pycompat.byteskwargs(opts)
4741 abort = opts.get(b'abort')
4745 abort = opts.get(b'abort')
4742 if abort and repo.dirstate.p2() == nullid:
4746 if abort and repo.dirstate.p2() == nullid:
4743 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4747 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4744 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4748 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4745 if abort:
4749 if abort:
4746 state = cmdutil.getunfinishedstate(repo)
4750 state = cmdutil.getunfinishedstate(repo)
4747 if state and state._opname != b'merge':
4751 if state and state._opname != b'merge':
4748 raise error.Abort(
4752 raise error.Abort(
4749 _(b'cannot abort merge with %s in progress') % (state._opname),
4753 _(b'cannot abort merge with %s in progress') % (state._opname),
4750 hint=state.hint(),
4754 hint=state.hint(),
4751 )
4755 )
4752 if node:
4756 if node:
4753 raise error.Abort(_(b"cannot specify a node with --abort"))
4757 raise error.Abort(_(b"cannot specify a node with --abort"))
4754 return hg.abortmerge(repo.ui, repo)
4758 return hg.abortmerge(repo.ui, repo)
4755
4759
4756 if opts.get(b'rev') and node:
4760 if opts.get(b'rev') and node:
4757 raise error.Abort(_(b"please specify just one revision"))
4761 raise error.Abort(_(b"please specify just one revision"))
4758 if not node:
4762 if not node:
4759 node = opts.get(b'rev')
4763 node = opts.get(b'rev')
4760
4764
4761 if node:
4765 if node:
4762 ctx = scmutil.revsingle(repo, node)
4766 ctx = scmutil.revsingle(repo, node)
4763 else:
4767 else:
4764 if ui.configbool(b'commands', b'merge.require-rev'):
4768 if ui.configbool(b'commands', b'merge.require-rev'):
4765 raise error.Abort(
4769 raise error.Abort(
4766 _(
4770 _(
4767 b'configuration requires specifying revision to merge '
4771 b'configuration requires specifying revision to merge '
4768 b'with'
4772 b'with'
4769 )
4773 )
4770 )
4774 )
4771 ctx = repo[destutil.destmerge(repo)]
4775 ctx = repo[destutil.destmerge(repo)]
4772
4776
4773 if ctx.node() is None:
4777 if ctx.node() is None:
4774 raise error.Abort(_(b'merging with the working copy has no effect'))
4778 raise error.Abort(_(b'merging with the working copy has no effect'))
4775
4779
4776 if opts.get(b'preview'):
4780 if opts.get(b'preview'):
4777 # find nodes that are ancestors of p2 but not of p1
4781 # find nodes that are ancestors of p2 but not of p1
4778 p1 = repo[b'.'].node()
4782 p1 = repo[b'.'].node()
4779 p2 = ctx.node()
4783 p2 = ctx.node()
4780 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4784 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4781
4785
4782 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4786 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4783 for node in nodes:
4787 for node in nodes:
4784 displayer.show(repo[node])
4788 displayer.show(repo[node])
4785 displayer.close()
4789 displayer.close()
4786 return 0
4790 return 0
4787
4791
4788 # ui.forcemerge is an internal variable, do not document
4792 # ui.forcemerge is an internal variable, do not document
4789 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4793 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4790 with ui.configoverride(overrides, b'merge'):
4794 with ui.configoverride(overrides, b'merge'):
4791 force = opts.get(b'force')
4795 force = opts.get(b'force')
4792 labels = [b'working copy', b'merge rev']
4796 labels = [b'working copy', b'merge rev']
4793 return hg.merge(ctx, force=force, labels=labels)
4797 return hg.merge(ctx, force=force, labels=labels)
4794
4798
4795
4799
4796 statemod.addunfinished(
4800 statemod.addunfinished(
4797 b'merge',
4801 b'merge',
4798 fname=None,
4802 fname=None,
4799 clearable=True,
4803 clearable=True,
4800 allowcommit=True,
4804 allowcommit=True,
4801 cmdmsg=_(b'outstanding uncommitted merge'),
4805 cmdmsg=_(b'outstanding uncommitted merge'),
4802 abortfunc=hg.abortmerge,
4806 abortfunc=hg.abortmerge,
4803 statushint=_(
4807 statushint=_(
4804 b'To continue: hg commit\nTo abort: hg merge --abort'
4808 b'To continue: hg commit\nTo abort: hg merge --abort'
4805 ),
4809 ),
4806 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4810 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4807 )
4811 )
4808
4812
4809
4813
4810 @command(
4814 @command(
4811 b'outgoing|out',
4815 b'outgoing|out',
4812 [
4816 [
4813 (
4817 (
4814 b'f',
4818 b'f',
4815 b'force',
4819 b'force',
4816 None,
4820 None,
4817 _(b'run even when the destination is unrelated'),
4821 _(b'run even when the destination is unrelated'),
4818 ),
4822 ),
4819 (
4823 (
4820 b'r',
4824 b'r',
4821 b'rev',
4825 b'rev',
4822 [],
4826 [],
4823 _(b'a changeset intended to be included in the destination'),
4827 _(b'a changeset intended to be included in the destination'),
4824 _(b'REV'),
4828 _(b'REV'),
4825 ),
4829 ),
4826 (b'n', b'newest-first', None, _(b'show newest record first')),
4830 (b'n', b'newest-first', None, _(b'show newest record first')),
4827 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4831 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4828 (
4832 (
4829 b'b',
4833 b'b',
4830 b'branch',
4834 b'branch',
4831 [],
4835 [],
4832 _(b'a specific branch you would like to push'),
4836 _(b'a specific branch you would like to push'),
4833 _(b'BRANCH'),
4837 _(b'BRANCH'),
4834 ),
4838 ),
4835 ]
4839 ]
4836 + logopts
4840 + logopts
4837 + remoteopts
4841 + remoteopts
4838 + subrepoopts,
4842 + subrepoopts,
4839 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4843 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4840 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4844 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4841 )
4845 )
4842 def outgoing(ui, repo, dest=None, **opts):
4846 def outgoing(ui, repo, dest=None, **opts):
4843 """show changesets not found in the destination
4847 """show changesets not found in the destination
4844
4848
4845 Show changesets not found in the specified destination repository
4849 Show changesets not found in the specified destination repository
4846 or the default push location. These are the changesets that would
4850 or the default push location. These are the changesets that would
4847 be pushed if a push was requested.
4851 be pushed if a push was requested.
4848
4852
4849 See pull for details of valid destination formats.
4853 See pull for details of valid destination formats.
4850
4854
4851 .. container:: verbose
4855 .. container:: verbose
4852
4856
4853 With -B/--bookmarks, the result of bookmark comparison between
4857 With -B/--bookmarks, the result of bookmark comparison between
4854 local and remote repositories is displayed. With -v/--verbose,
4858 local and remote repositories is displayed. With -v/--verbose,
4855 status is also displayed for each bookmark like below::
4859 status is also displayed for each bookmark like below::
4856
4860
4857 BM1 01234567890a added
4861 BM1 01234567890a added
4858 BM2 deleted
4862 BM2 deleted
4859 BM3 234567890abc advanced
4863 BM3 234567890abc advanced
4860 BM4 34567890abcd diverged
4864 BM4 34567890abcd diverged
4861 BM5 4567890abcde changed
4865 BM5 4567890abcde changed
4862
4866
4863 The action taken when pushing depends on the
4867 The action taken when pushing depends on the
4864 status of each bookmark:
4868 status of each bookmark:
4865
4869
4866 :``added``: push with ``-B`` will create it
4870 :``added``: push with ``-B`` will create it
4867 :``deleted``: push with ``-B`` will delete it
4871 :``deleted``: push with ``-B`` will delete it
4868 :``advanced``: push will update it
4872 :``advanced``: push will update it
4869 :``diverged``: push with ``-B`` will update it
4873 :``diverged``: push with ``-B`` will update it
4870 :``changed``: push with ``-B`` will update it
4874 :``changed``: push with ``-B`` will update it
4871
4875
4872 From the point of view of pushing behavior, bookmarks
4876 From the point of view of pushing behavior, bookmarks
4873 existing only in the remote repository are treated as
4877 existing only in the remote repository are treated as
4874 ``deleted``, even if it is in fact added remotely.
4878 ``deleted``, even if it is in fact added remotely.
4875
4879
4876 Returns 0 if there are outgoing changes, 1 otherwise.
4880 Returns 0 if there are outgoing changes, 1 otherwise.
4877 """
4881 """
4878 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4882 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4879 # style URLs, so don't overwrite dest.
4883 # style URLs, so don't overwrite dest.
4880 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4884 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4881 if not path:
4885 if not path:
4882 raise error.Abort(
4886 raise error.Abort(
4883 _(b'default repository not configured!'),
4887 _(b'default repository not configured!'),
4884 hint=_(b"see 'hg help config.paths'"),
4888 hint=_(b"see 'hg help config.paths'"),
4885 )
4889 )
4886
4890
4887 opts = pycompat.byteskwargs(opts)
4891 opts = pycompat.byteskwargs(opts)
4888 if opts.get(b'graph'):
4892 if opts.get(b'graph'):
4889 logcmdutil.checkunsupportedgraphflags([], opts)
4893 logcmdutil.checkunsupportedgraphflags([], opts)
4890 o, other = hg._outgoing(ui, repo, dest, opts)
4894 o, other = hg._outgoing(ui, repo, dest, opts)
4891 if not o:
4895 if not o:
4892 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4896 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4893 return
4897 return
4894
4898
4895 revdag = logcmdutil.graphrevs(repo, o, opts)
4899 revdag = logcmdutil.graphrevs(repo, o, opts)
4896 ui.pager(b'outgoing')
4900 ui.pager(b'outgoing')
4897 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4901 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4898 logcmdutil.displaygraph(
4902 logcmdutil.displaygraph(
4899 ui, repo, revdag, displayer, graphmod.asciiedges
4903 ui, repo, revdag, displayer, graphmod.asciiedges
4900 )
4904 )
4901 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4905 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4902 return 0
4906 return 0
4903
4907
4904 if opts.get(b'bookmarks'):
4908 if opts.get(b'bookmarks'):
4905 dest = path.pushloc or path.loc
4909 dest = path.pushloc or path.loc
4906 other = hg.peer(repo, opts, dest)
4910 other = hg.peer(repo, opts, dest)
4907 if b'bookmarks' not in other.listkeys(b'namespaces'):
4911 if b'bookmarks' not in other.listkeys(b'namespaces'):
4908 ui.warn(_(b"remote doesn't support bookmarks\n"))
4912 ui.warn(_(b"remote doesn't support bookmarks\n"))
4909 return 0
4913 return 0
4910 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4914 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4911 ui.pager(b'outgoing')
4915 ui.pager(b'outgoing')
4912 return bookmarks.outgoing(ui, repo, other)
4916 return bookmarks.outgoing(ui, repo, other)
4913
4917
4914 repo._subtoppath = path.pushloc or path.loc
4918 repo._subtoppath = path.pushloc or path.loc
4915 try:
4919 try:
4916 return hg.outgoing(ui, repo, dest, opts)
4920 return hg.outgoing(ui, repo, dest, opts)
4917 finally:
4921 finally:
4918 del repo._subtoppath
4922 del repo._subtoppath
4919
4923
4920
4924
4921 @command(
4925 @command(
4922 b'parents',
4926 b'parents',
4923 [
4927 [
4924 (
4928 (
4925 b'r',
4929 b'r',
4926 b'rev',
4930 b'rev',
4927 b'',
4931 b'',
4928 _(b'show parents of the specified revision'),
4932 _(b'show parents of the specified revision'),
4929 _(b'REV'),
4933 _(b'REV'),
4930 ),
4934 ),
4931 ]
4935 ]
4932 + templateopts,
4936 + templateopts,
4933 _(b'[-r REV] [FILE]'),
4937 _(b'[-r REV] [FILE]'),
4934 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4938 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4935 inferrepo=True,
4939 inferrepo=True,
4936 )
4940 )
4937 def parents(ui, repo, file_=None, **opts):
4941 def parents(ui, repo, file_=None, **opts):
4938 """show the parents of the working directory or revision (DEPRECATED)
4942 """show the parents of the working directory or revision (DEPRECATED)
4939
4943
4940 Print the working directory's parent revisions. If a revision is
4944 Print the working directory's parent revisions. If a revision is
4941 given via -r/--rev, the parent of that revision will be printed.
4945 given via -r/--rev, the parent of that revision will be printed.
4942 If a file argument is given, the revision in which the file was
4946 If a file argument is given, the revision in which the file was
4943 last changed (before the working directory revision or the
4947 last changed (before the working directory revision or the
4944 argument to --rev if given) is printed.
4948 argument to --rev if given) is printed.
4945
4949
4946 This command is equivalent to::
4950 This command is equivalent to::
4947
4951
4948 hg log -r "p1()+p2()" or
4952 hg log -r "p1()+p2()" or
4949 hg log -r "p1(REV)+p2(REV)" or
4953 hg log -r "p1(REV)+p2(REV)" or
4950 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4954 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4951 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4955 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4952
4956
4953 See :hg:`summary` and :hg:`help revsets` for related information.
4957 See :hg:`summary` and :hg:`help revsets` for related information.
4954
4958
4955 Returns 0 on success.
4959 Returns 0 on success.
4956 """
4960 """
4957
4961
4958 opts = pycompat.byteskwargs(opts)
4962 opts = pycompat.byteskwargs(opts)
4959 rev = opts.get(b'rev')
4963 rev = opts.get(b'rev')
4960 if rev:
4964 if rev:
4961 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
4965 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
4962 ctx = scmutil.revsingle(repo, rev, None)
4966 ctx = scmutil.revsingle(repo, rev, None)
4963
4967
4964 if file_:
4968 if file_:
4965 m = scmutil.match(ctx, (file_,), opts)
4969 m = scmutil.match(ctx, (file_,), opts)
4966 if m.anypats() or len(m.files()) != 1:
4970 if m.anypats() or len(m.files()) != 1:
4967 raise error.Abort(_(b'can only specify an explicit filename'))
4971 raise error.Abort(_(b'can only specify an explicit filename'))
4968 file_ = m.files()[0]
4972 file_ = m.files()[0]
4969 filenodes = []
4973 filenodes = []
4970 for cp in ctx.parents():
4974 for cp in ctx.parents():
4971 if not cp:
4975 if not cp:
4972 continue
4976 continue
4973 try:
4977 try:
4974 filenodes.append(cp.filenode(file_))
4978 filenodes.append(cp.filenode(file_))
4975 except error.LookupError:
4979 except error.LookupError:
4976 pass
4980 pass
4977 if not filenodes:
4981 if not filenodes:
4978 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
4982 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
4979 p = []
4983 p = []
4980 for fn in filenodes:
4984 for fn in filenodes:
4981 fctx = repo.filectx(file_, fileid=fn)
4985 fctx = repo.filectx(file_, fileid=fn)
4982 p.append(fctx.node())
4986 p.append(fctx.node())
4983 else:
4987 else:
4984 p = [cp.node() for cp in ctx.parents()]
4988 p = [cp.node() for cp in ctx.parents()]
4985
4989
4986 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4990 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4987 for n in p:
4991 for n in p:
4988 if n != nullid:
4992 if n != nullid:
4989 displayer.show(repo[n])
4993 displayer.show(repo[n])
4990 displayer.close()
4994 displayer.close()
4991
4995
4992
4996
4993 @command(
4997 @command(
4994 b'paths',
4998 b'paths',
4995 formatteropts,
4999 formatteropts,
4996 _(b'[NAME]'),
5000 _(b'[NAME]'),
4997 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5001 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4998 optionalrepo=True,
5002 optionalrepo=True,
4999 intents={INTENT_READONLY},
5003 intents={INTENT_READONLY},
5000 )
5004 )
5001 def paths(ui, repo, search=None, **opts):
5005 def paths(ui, repo, search=None, **opts):
5002 """show aliases for remote repositories
5006 """show aliases for remote repositories
5003
5007
5004 Show definition of symbolic path name NAME. If no name is given,
5008 Show definition of symbolic path name NAME. If no name is given,
5005 show definition of all available names.
5009 show definition of all available names.
5006
5010
5007 Option -q/--quiet suppresses all output when searching for NAME
5011 Option -q/--quiet suppresses all output when searching for NAME
5008 and shows only the path names when listing all definitions.
5012 and shows only the path names when listing all definitions.
5009
5013
5010 Path names are defined in the [paths] section of your
5014 Path names are defined in the [paths] section of your
5011 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5015 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5012 repository, ``.hg/hgrc`` is used, too.
5016 repository, ``.hg/hgrc`` is used, too.
5013
5017
5014 The path names ``default`` and ``default-push`` have a special
5018 The path names ``default`` and ``default-push`` have a special
5015 meaning. When performing a push or pull operation, they are used
5019 meaning. When performing a push or pull operation, they are used
5016 as fallbacks if no location is specified on the command-line.
5020 as fallbacks if no location is specified on the command-line.
5017 When ``default-push`` is set, it will be used for push and
5021 When ``default-push`` is set, it will be used for push and
5018 ``default`` will be used for pull; otherwise ``default`` is used
5022 ``default`` will be used for pull; otherwise ``default`` is used
5019 as the fallback for both. When cloning a repository, the clone
5023 as the fallback for both. When cloning a repository, the clone
5020 source is written as ``default`` in ``.hg/hgrc``.
5024 source is written as ``default`` in ``.hg/hgrc``.
5021
5025
5022 .. note::
5026 .. note::
5023
5027
5024 ``default`` and ``default-push`` apply to all inbound (e.g.
5028 ``default`` and ``default-push`` apply to all inbound (e.g.
5025 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5029 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5026 and :hg:`bundle`) operations.
5030 and :hg:`bundle`) operations.
5027
5031
5028 See :hg:`help urls` for more information.
5032 See :hg:`help urls` for more information.
5029
5033
5030 .. container:: verbose
5034 .. container:: verbose
5031
5035
5032 Template:
5036 Template:
5033
5037
5034 The following keywords are supported. See also :hg:`help templates`.
5038 The following keywords are supported. See also :hg:`help templates`.
5035
5039
5036 :name: String. Symbolic name of the path alias.
5040 :name: String. Symbolic name of the path alias.
5037 :pushurl: String. URL for push operations.
5041 :pushurl: String. URL for push operations.
5038 :url: String. URL or directory path for the other operations.
5042 :url: String. URL or directory path for the other operations.
5039
5043
5040 Returns 0 on success.
5044 Returns 0 on success.
5041 """
5045 """
5042
5046
5043 opts = pycompat.byteskwargs(opts)
5047 opts = pycompat.byteskwargs(opts)
5044 ui.pager(b'paths')
5048 ui.pager(b'paths')
5045 if search:
5049 if search:
5046 pathitems = [
5050 pathitems = [
5047 (name, path)
5051 (name, path)
5048 for name, path in pycompat.iteritems(ui.paths)
5052 for name, path in pycompat.iteritems(ui.paths)
5049 if name == search
5053 if name == search
5050 ]
5054 ]
5051 else:
5055 else:
5052 pathitems = sorted(pycompat.iteritems(ui.paths))
5056 pathitems = sorted(pycompat.iteritems(ui.paths))
5053
5057
5054 fm = ui.formatter(b'paths', opts)
5058 fm = ui.formatter(b'paths', opts)
5055 if fm.isplain():
5059 if fm.isplain():
5056 hidepassword = util.hidepassword
5060 hidepassword = util.hidepassword
5057 else:
5061 else:
5058 hidepassword = bytes
5062 hidepassword = bytes
5059 if ui.quiet:
5063 if ui.quiet:
5060 namefmt = b'%s\n'
5064 namefmt = b'%s\n'
5061 else:
5065 else:
5062 namefmt = b'%s = '
5066 namefmt = b'%s = '
5063 showsubopts = not search and not ui.quiet
5067 showsubopts = not search and not ui.quiet
5064
5068
5065 for name, path in pathitems:
5069 for name, path in pathitems:
5066 fm.startitem()
5070 fm.startitem()
5067 fm.condwrite(not search, b'name', namefmt, name)
5071 fm.condwrite(not search, b'name', namefmt, name)
5068 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5072 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5069 for subopt, value in sorted(path.suboptions.items()):
5073 for subopt, value in sorted(path.suboptions.items()):
5070 assert subopt not in (b'name', b'url')
5074 assert subopt not in (b'name', b'url')
5071 if showsubopts:
5075 if showsubopts:
5072 fm.plain(b'%s:%s = ' % (name, subopt))
5076 fm.plain(b'%s:%s = ' % (name, subopt))
5073 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5077 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5074
5078
5075 fm.end()
5079 fm.end()
5076
5080
5077 if search and not pathitems:
5081 if search and not pathitems:
5078 if not ui.quiet:
5082 if not ui.quiet:
5079 ui.warn(_(b"not found!\n"))
5083 ui.warn(_(b"not found!\n"))
5080 return 1
5084 return 1
5081 else:
5085 else:
5082 return 0
5086 return 0
5083
5087
5084
5088
5085 @command(
5089 @command(
5086 b'phase',
5090 b'phase',
5087 [
5091 [
5088 (b'p', b'public', False, _(b'set changeset phase to public')),
5092 (b'p', b'public', False, _(b'set changeset phase to public')),
5089 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5093 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5090 (b's', b'secret', False, _(b'set changeset phase to secret')),
5094 (b's', b'secret', False, _(b'set changeset phase to secret')),
5091 (b'f', b'force', False, _(b'allow to move boundary backward')),
5095 (b'f', b'force', False, _(b'allow to move boundary backward')),
5092 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5096 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5093 ],
5097 ],
5094 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5098 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5095 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5099 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5096 )
5100 )
5097 def phase(ui, repo, *revs, **opts):
5101 def phase(ui, repo, *revs, **opts):
5098 """set or show the current phase name
5102 """set or show the current phase name
5099
5103
5100 With no argument, show the phase name of the current revision(s).
5104 With no argument, show the phase name of the current revision(s).
5101
5105
5102 With one of -p/--public, -d/--draft or -s/--secret, change the
5106 With one of -p/--public, -d/--draft or -s/--secret, change the
5103 phase value of the specified revisions.
5107 phase value of the specified revisions.
5104
5108
5105 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5109 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5106 lower phase to a higher phase. Phases are ordered as follows::
5110 lower phase to a higher phase. Phases are ordered as follows::
5107
5111
5108 public < draft < secret
5112 public < draft < secret
5109
5113
5110 Returns 0 on success, 1 if some phases could not be changed.
5114 Returns 0 on success, 1 if some phases could not be changed.
5111
5115
5112 (For more information about the phases concept, see :hg:`help phases`.)
5116 (For more information about the phases concept, see :hg:`help phases`.)
5113 """
5117 """
5114 opts = pycompat.byteskwargs(opts)
5118 opts = pycompat.byteskwargs(opts)
5115 # search for a unique phase argument
5119 # search for a unique phase argument
5116 targetphase = None
5120 targetphase = None
5117 for idx, name in enumerate(phases.cmdphasenames):
5121 for idx, name in enumerate(phases.cmdphasenames):
5118 if opts[name]:
5122 if opts[name]:
5119 if targetphase is not None:
5123 if targetphase is not None:
5120 raise error.Abort(_(b'only one phase can be specified'))
5124 raise error.Abort(_(b'only one phase can be specified'))
5121 targetphase = idx
5125 targetphase = idx
5122
5126
5123 # look for specified revision
5127 # look for specified revision
5124 revs = list(revs)
5128 revs = list(revs)
5125 revs.extend(opts[b'rev'])
5129 revs.extend(opts[b'rev'])
5126 if not revs:
5130 if not revs:
5127 # display both parents as the second parent phase can influence
5131 # display both parents as the second parent phase can influence
5128 # the phase of a merge commit
5132 # the phase of a merge commit
5129 revs = [c.rev() for c in repo[None].parents()]
5133 revs = [c.rev() for c in repo[None].parents()]
5130
5134
5131 revs = scmutil.revrange(repo, revs)
5135 revs = scmutil.revrange(repo, revs)
5132
5136
5133 ret = 0
5137 ret = 0
5134 if targetphase is None:
5138 if targetphase is None:
5135 # display
5139 # display
5136 for r in revs:
5140 for r in revs:
5137 ctx = repo[r]
5141 ctx = repo[r]
5138 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5142 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5139 else:
5143 else:
5140 with repo.lock(), repo.transaction(b"phase") as tr:
5144 with repo.lock(), repo.transaction(b"phase") as tr:
5141 # set phase
5145 # set phase
5142 if not revs:
5146 if not revs:
5143 raise error.Abort(_(b'empty revision set'))
5147 raise error.Abort(_(b'empty revision set'))
5144 nodes = [repo[r].node() for r in revs]
5148 nodes = [repo[r].node() for r in revs]
5145 # moving revision from public to draft may hide them
5149 # moving revision from public to draft may hide them
5146 # We have to check result on an unfiltered repository
5150 # We have to check result on an unfiltered repository
5147 unfi = repo.unfiltered()
5151 unfi = repo.unfiltered()
5148 getphase = unfi._phasecache.phase
5152 getphase = unfi._phasecache.phase
5149 olddata = [getphase(unfi, r) for r in unfi]
5153 olddata = [getphase(unfi, r) for r in unfi]
5150 phases.advanceboundary(repo, tr, targetphase, nodes)
5154 phases.advanceboundary(repo, tr, targetphase, nodes)
5151 if opts[b'force']:
5155 if opts[b'force']:
5152 phases.retractboundary(repo, tr, targetphase, nodes)
5156 phases.retractboundary(repo, tr, targetphase, nodes)
5153 getphase = unfi._phasecache.phase
5157 getphase = unfi._phasecache.phase
5154 newdata = [getphase(unfi, r) for r in unfi]
5158 newdata = [getphase(unfi, r) for r in unfi]
5155 changes = sum(newdata[r] != olddata[r] for r in unfi)
5159 changes = sum(newdata[r] != olddata[r] for r in unfi)
5156 cl = unfi.changelog
5160 cl = unfi.changelog
5157 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5161 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5158 if rejected:
5162 if rejected:
5159 ui.warn(
5163 ui.warn(
5160 _(
5164 _(
5161 b'cannot move %i changesets to a higher '
5165 b'cannot move %i changesets to a higher '
5162 b'phase, use --force\n'
5166 b'phase, use --force\n'
5163 )
5167 )
5164 % len(rejected)
5168 % len(rejected)
5165 )
5169 )
5166 ret = 1
5170 ret = 1
5167 if changes:
5171 if changes:
5168 msg = _(b'phase changed for %i changesets\n') % changes
5172 msg = _(b'phase changed for %i changesets\n') % changes
5169 if ret:
5173 if ret:
5170 ui.status(msg)
5174 ui.status(msg)
5171 else:
5175 else:
5172 ui.note(msg)
5176 ui.note(msg)
5173 else:
5177 else:
5174 ui.warn(_(b'no phases changed\n'))
5178 ui.warn(_(b'no phases changed\n'))
5175 return ret
5179 return ret
5176
5180
5177
5181
5178 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5182 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5179 """Run after a changegroup has been added via pull/unbundle
5183 """Run after a changegroup has been added via pull/unbundle
5180
5184
5181 This takes arguments below:
5185 This takes arguments below:
5182
5186
5183 :modheads: change of heads by pull/unbundle
5187 :modheads: change of heads by pull/unbundle
5184 :optupdate: updating working directory is needed or not
5188 :optupdate: updating working directory is needed or not
5185 :checkout: update destination revision (or None to default destination)
5189 :checkout: update destination revision (or None to default destination)
5186 :brev: a name, which might be a bookmark to be activated after updating
5190 :brev: a name, which might be a bookmark to be activated after updating
5187 """
5191 """
5188 if modheads == 0:
5192 if modheads == 0:
5189 return
5193 return
5190 if optupdate:
5194 if optupdate:
5191 try:
5195 try:
5192 return hg.updatetotally(ui, repo, checkout, brev)
5196 return hg.updatetotally(ui, repo, checkout, brev)
5193 except error.UpdateAbort as inst:
5197 except error.UpdateAbort as inst:
5194 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5198 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5195 hint = inst.hint
5199 hint = inst.hint
5196 raise error.UpdateAbort(msg, hint=hint)
5200 raise error.UpdateAbort(msg, hint=hint)
5197 if modheads is not None and modheads > 1:
5201 if modheads is not None and modheads > 1:
5198 currentbranchheads = len(repo.branchheads())
5202 currentbranchheads = len(repo.branchheads())
5199 if currentbranchheads == modheads:
5203 if currentbranchheads == modheads:
5200 ui.status(
5204 ui.status(
5201 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5205 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5202 )
5206 )
5203 elif currentbranchheads > 1:
5207 elif currentbranchheads > 1:
5204 ui.status(
5208 ui.status(
5205 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5209 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5206 )
5210 )
5207 else:
5211 else:
5208 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5212 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5209 elif not ui.configbool(b'commands', b'update.requiredest'):
5213 elif not ui.configbool(b'commands', b'update.requiredest'):
5210 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5214 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5211
5215
5212
5216
5213 @command(
5217 @command(
5214 b'pull',
5218 b'pull',
5215 [
5219 [
5216 (
5220 (
5217 b'u',
5221 b'u',
5218 b'update',
5222 b'update',
5219 None,
5223 None,
5220 _(b'update to new branch head if new descendants were pulled'),
5224 _(b'update to new branch head if new descendants were pulled'),
5221 ),
5225 ),
5222 (
5226 (
5223 b'f',
5227 b'f',
5224 b'force',
5228 b'force',
5225 None,
5229 None,
5226 _(b'run even when remote repository is unrelated'),
5230 _(b'run even when remote repository is unrelated'),
5227 ),
5231 ),
5228 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5232 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5229 (
5233 (
5230 b'r',
5234 b'r',
5231 b'rev',
5235 b'rev',
5232 [],
5236 [],
5233 _(b'a remote changeset intended to be added'),
5237 _(b'a remote changeset intended to be added'),
5234 _(b'REV'),
5238 _(b'REV'),
5235 ),
5239 ),
5236 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5240 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5237 (
5241 (
5238 b'b',
5242 b'b',
5239 b'branch',
5243 b'branch',
5240 [],
5244 [],
5241 _(b'a specific branch you would like to pull'),
5245 _(b'a specific branch you would like to pull'),
5242 _(b'BRANCH'),
5246 _(b'BRANCH'),
5243 ),
5247 ),
5244 ]
5248 ]
5245 + remoteopts,
5249 + remoteopts,
5246 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5250 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5247 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5251 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5248 helpbasic=True,
5252 helpbasic=True,
5249 )
5253 )
5250 def pull(ui, repo, source=b"default", **opts):
5254 def pull(ui, repo, source=b"default", **opts):
5251 """pull changes from the specified source
5255 """pull changes from the specified source
5252
5256
5253 Pull changes from a remote repository to a local one.
5257 Pull changes from a remote repository to a local one.
5254
5258
5255 This finds all changes from the repository at the specified path
5259 This finds all changes from the repository at the specified path
5256 or URL and adds them to a local repository (the current one unless
5260 or URL and adds them to a local repository (the current one unless
5257 -R is specified). By default, this does not update the copy of the
5261 -R is specified). By default, this does not update the copy of the
5258 project in the working directory.
5262 project in the working directory.
5259
5263
5260 When cloning from servers that support it, Mercurial may fetch
5264 When cloning from servers that support it, Mercurial may fetch
5261 pre-generated data. When this is done, hooks operating on incoming
5265 pre-generated data. When this is done, hooks operating on incoming
5262 changesets and changegroups may fire more than once, once for each
5266 changesets and changegroups may fire more than once, once for each
5263 pre-generated bundle and as well as for any additional remaining
5267 pre-generated bundle and as well as for any additional remaining
5264 data. See :hg:`help -e clonebundles` for more.
5268 data. See :hg:`help -e clonebundles` for more.
5265
5269
5266 Use :hg:`incoming` if you want to see what would have been added
5270 Use :hg:`incoming` if you want to see what would have been added
5267 by a pull at the time you issued this command. If you then decide
5271 by a pull at the time you issued this command. If you then decide
5268 to add those changes to the repository, you should use :hg:`pull
5272 to add those changes to the repository, you should use :hg:`pull
5269 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5273 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5270
5274
5271 If SOURCE is omitted, the 'default' path will be used.
5275 If SOURCE is omitted, the 'default' path will be used.
5272 See :hg:`help urls` for more information.
5276 See :hg:`help urls` for more information.
5273
5277
5274 Specifying bookmark as ``.`` is equivalent to specifying the active
5278 Specifying bookmark as ``.`` is equivalent to specifying the active
5275 bookmark's name.
5279 bookmark's name.
5276
5280
5277 Returns 0 on success, 1 if an update had unresolved files.
5281 Returns 0 on success, 1 if an update had unresolved files.
5278 """
5282 """
5279
5283
5280 opts = pycompat.byteskwargs(opts)
5284 opts = pycompat.byteskwargs(opts)
5281 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5285 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5282 b'update'
5286 b'update'
5283 ):
5287 ):
5284 msg = _(b'update destination required by configuration')
5288 msg = _(b'update destination required by configuration')
5285 hint = _(b'use hg pull followed by hg update DEST')
5289 hint = _(b'use hg pull followed by hg update DEST')
5286 raise error.Abort(msg, hint=hint)
5290 raise error.Abort(msg, hint=hint)
5287
5291
5288 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5292 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5289 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5293 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5290 other = hg.peer(repo, opts, source)
5294 other = hg.peer(repo, opts, source)
5291 try:
5295 try:
5292 revs, checkout = hg.addbranchrevs(
5296 revs, checkout = hg.addbranchrevs(
5293 repo, other, branches, opts.get(b'rev')
5297 repo, other, branches, opts.get(b'rev')
5294 )
5298 )
5295
5299
5296 pullopargs = {}
5300 pullopargs = {}
5297
5301
5298 nodes = None
5302 nodes = None
5299 if opts.get(b'bookmark') or revs:
5303 if opts.get(b'bookmark') or revs:
5300 # The list of bookmark used here is the same used to actually update
5304 # The list of bookmark used here is the same used to actually update
5301 # the bookmark names, to avoid the race from issue 4689 and we do
5305 # the bookmark names, to avoid the race from issue 4689 and we do
5302 # all lookup and bookmark queries in one go so they see the same
5306 # all lookup and bookmark queries in one go so they see the same
5303 # version of the server state (issue 4700).
5307 # version of the server state (issue 4700).
5304 nodes = []
5308 nodes = []
5305 fnodes = []
5309 fnodes = []
5306 revs = revs or []
5310 revs = revs or []
5307 if revs and not other.capable(b'lookup'):
5311 if revs and not other.capable(b'lookup'):
5308 err = _(
5312 err = _(
5309 b"other repository doesn't support revision lookup, "
5313 b"other repository doesn't support revision lookup, "
5310 b"so a rev cannot be specified."
5314 b"so a rev cannot be specified."
5311 )
5315 )
5312 raise error.Abort(err)
5316 raise error.Abort(err)
5313 with other.commandexecutor() as e:
5317 with other.commandexecutor() as e:
5314 fremotebookmarks = e.callcommand(
5318 fremotebookmarks = e.callcommand(
5315 b'listkeys', {b'namespace': b'bookmarks'}
5319 b'listkeys', {b'namespace': b'bookmarks'}
5316 )
5320 )
5317 for r in revs:
5321 for r in revs:
5318 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5322 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5319 remotebookmarks = fremotebookmarks.result()
5323 remotebookmarks = fremotebookmarks.result()
5320 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5324 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5321 pullopargs[b'remotebookmarks'] = remotebookmarks
5325 pullopargs[b'remotebookmarks'] = remotebookmarks
5322 for b in opts.get(b'bookmark', []):
5326 for b in opts.get(b'bookmark', []):
5323 b = repo._bookmarks.expandname(b)
5327 b = repo._bookmarks.expandname(b)
5324 if b not in remotebookmarks:
5328 if b not in remotebookmarks:
5325 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5329 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5326 nodes.append(remotebookmarks[b])
5330 nodes.append(remotebookmarks[b])
5327 for i, rev in enumerate(revs):
5331 for i, rev in enumerate(revs):
5328 node = fnodes[i].result()
5332 node = fnodes[i].result()
5329 nodes.append(node)
5333 nodes.append(node)
5330 if rev == checkout:
5334 if rev == checkout:
5331 checkout = node
5335 checkout = node
5332
5336
5333 wlock = util.nullcontextmanager()
5337 wlock = util.nullcontextmanager()
5334 if opts.get(b'update'):
5338 if opts.get(b'update'):
5335 wlock = repo.wlock()
5339 wlock = repo.wlock()
5336 with wlock:
5340 with wlock:
5337 pullopargs.update(opts.get(b'opargs', {}))
5341 pullopargs.update(opts.get(b'opargs', {}))
5338 modheads = exchange.pull(
5342 modheads = exchange.pull(
5339 repo,
5343 repo,
5340 other,
5344 other,
5341 heads=nodes,
5345 heads=nodes,
5342 force=opts.get(b'force'),
5346 force=opts.get(b'force'),
5343 bookmarks=opts.get(b'bookmark', ()),
5347 bookmarks=opts.get(b'bookmark', ()),
5344 opargs=pullopargs,
5348 opargs=pullopargs,
5345 confirm=opts.get(b'confirm'),
5349 confirm=opts.get(b'confirm'),
5346 ).cgresult
5350 ).cgresult
5347
5351
5348 # brev is a name, which might be a bookmark to be activated at
5352 # brev is a name, which might be a bookmark to be activated at
5349 # the end of the update. In other words, it is an explicit
5353 # the end of the update. In other words, it is an explicit
5350 # destination of the update
5354 # destination of the update
5351 brev = None
5355 brev = None
5352
5356
5353 if checkout:
5357 if checkout:
5354 checkout = repo.unfiltered().changelog.rev(checkout)
5358 checkout = repo.unfiltered().changelog.rev(checkout)
5355
5359
5356 # order below depends on implementation of
5360 # order below depends on implementation of
5357 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5361 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5358 # because 'checkout' is determined without it.
5362 # because 'checkout' is determined without it.
5359 if opts.get(b'rev'):
5363 if opts.get(b'rev'):
5360 brev = opts[b'rev'][0]
5364 brev = opts[b'rev'][0]
5361 elif opts.get(b'branch'):
5365 elif opts.get(b'branch'):
5362 brev = opts[b'branch'][0]
5366 brev = opts[b'branch'][0]
5363 else:
5367 else:
5364 brev = branches[0]
5368 brev = branches[0]
5365 repo._subtoppath = source
5369 repo._subtoppath = source
5366 try:
5370 try:
5367 ret = postincoming(
5371 ret = postincoming(
5368 ui, repo, modheads, opts.get(b'update'), checkout, brev
5372 ui, repo, modheads, opts.get(b'update'), checkout, brev
5369 )
5373 )
5370 except error.FilteredRepoLookupError as exc:
5374 except error.FilteredRepoLookupError as exc:
5371 msg = _(b'cannot update to target: %s') % exc.args[0]
5375 msg = _(b'cannot update to target: %s') % exc.args[0]
5372 exc.args = (msg,) + exc.args[1:]
5376 exc.args = (msg,) + exc.args[1:]
5373 raise
5377 raise
5374 finally:
5378 finally:
5375 del repo._subtoppath
5379 del repo._subtoppath
5376
5380
5377 finally:
5381 finally:
5378 other.close()
5382 other.close()
5379 return ret
5383 return ret
5380
5384
5381
5385
5382 @command(
5386 @command(
5383 b'push',
5387 b'push',
5384 [
5388 [
5385 (b'f', b'force', None, _(b'force push')),
5389 (b'f', b'force', None, _(b'force push')),
5386 (
5390 (
5387 b'r',
5391 b'r',
5388 b'rev',
5392 b'rev',
5389 [],
5393 [],
5390 _(b'a changeset intended to be included in the destination'),
5394 _(b'a changeset intended to be included in the destination'),
5391 _(b'REV'),
5395 _(b'REV'),
5392 ),
5396 ),
5393 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5397 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5394 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5398 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5395 (
5399 (
5396 b'b',
5400 b'b',
5397 b'branch',
5401 b'branch',
5398 [],
5402 [],
5399 _(b'a specific branch you would like to push'),
5403 _(b'a specific branch you would like to push'),
5400 _(b'BRANCH'),
5404 _(b'BRANCH'),
5401 ),
5405 ),
5402 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5406 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5403 (
5407 (
5404 b'',
5408 b'',
5405 b'pushvars',
5409 b'pushvars',
5406 [],
5410 [],
5407 _(b'variables that can be sent to server (ADVANCED)'),
5411 _(b'variables that can be sent to server (ADVANCED)'),
5408 ),
5412 ),
5409 (
5413 (
5410 b'',
5414 b'',
5411 b'publish',
5415 b'publish',
5412 False,
5416 False,
5413 _(b'push the changeset as public (EXPERIMENTAL)'),
5417 _(b'push the changeset as public (EXPERIMENTAL)'),
5414 ),
5418 ),
5415 ]
5419 ]
5416 + remoteopts,
5420 + remoteopts,
5417 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5421 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5418 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5422 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5419 helpbasic=True,
5423 helpbasic=True,
5420 )
5424 )
5421 def push(ui, repo, dest=None, **opts):
5425 def push(ui, repo, dest=None, **opts):
5422 """push changes to the specified destination
5426 """push changes to the specified destination
5423
5427
5424 Push changesets from the local repository to the specified
5428 Push changesets from the local repository to the specified
5425 destination.
5429 destination.
5426
5430
5427 This operation is symmetrical to pull: it is identical to a pull
5431 This operation is symmetrical to pull: it is identical to a pull
5428 in the destination repository from the current one.
5432 in the destination repository from the current one.
5429
5433
5430 By default, push will not allow creation of new heads at the
5434 By default, push will not allow creation of new heads at the
5431 destination, since multiple heads would make it unclear which head
5435 destination, since multiple heads would make it unclear which head
5432 to use. In this situation, it is recommended to pull and merge
5436 to use. In this situation, it is recommended to pull and merge
5433 before pushing.
5437 before pushing.
5434
5438
5435 Use --new-branch if you want to allow push to create a new named
5439 Use --new-branch if you want to allow push to create a new named
5436 branch that is not present at the destination. This allows you to
5440 branch that is not present at the destination. This allows you to
5437 only create a new branch without forcing other changes.
5441 only create a new branch without forcing other changes.
5438
5442
5439 .. note::
5443 .. note::
5440
5444
5441 Extra care should be taken with the -f/--force option,
5445 Extra care should be taken with the -f/--force option,
5442 which will push all new heads on all branches, an action which will
5446 which will push all new heads on all branches, an action which will
5443 almost always cause confusion for collaborators.
5447 almost always cause confusion for collaborators.
5444
5448
5445 If -r/--rev is used, the specified revision and all its ancestors
5449 If -r/--rev is used, the specified revision and all its ancestors
5446 will be pushed to the remote repository.
5450 will be pushed to the remote repository.
5447
5451
5448 If -B/--bookmark is used, the specified bookmarked revision, its
5452 If -B/--bookmark is used, the specified bookmarked revision, its
5449 ancestors, and the bookmark will be pushed to the remote
5453 ancestors, and the bookmark will be pushed to the remote
5450 repository. Specifying ``.`` is equivalent to specifying the active
5454 repository. Specifying ``.`` is equivalent to specifying the active
5451 bookmark's name. Use the --all-bookmarks option for pushing all
5455 bookmark's name. Use the --all-bookmarks option for pushing all
5452 current bookmarks.
5456 current bookmarks.
5453
5457
5454 Please see :hg:`help urls` for important details about ``ssh://``
5458 Please see :hg:`help urls` for important details about ``ssh://``
5455 URLs. If DESTINATION is omitted, a default path will be used.
5459 URLs. If DESTINATION is omitted, a default path will be used.
5456
5460
5457 .. container:: verbose
5461 .. container:: verbose
5458
5462
5459 The --pushvars option sends strings to the server that become
5463 The --pushvars option sends strings to the server that become
5460 environment variables prepended with ``HG_USERVAR_``. For example,
5464 environment variables prepended with ``HG_USERVAR_``. For example,
5461 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5465 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5462 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5466 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5463
5467
5464 pushvars can provide for user-overridable hooks as well as set debug
5468 pushvars can provide for user-overridable hooks as well as set debug
5465 levels. One example is having a hook that blocks commits containing
5469 levels. One example is having a hook that blocks commits containing
5466 conflict markers, but enables the user to override the hook if the file
5470 conflict markers, but enables the user to override the hook if the file
5467 is using conflict markers for testing purposes or the file format has
5471 is using conflict markers for testing purposes or the file format has
5468 strings that look like conflict markers.
5472 strings that look like conflict markers.
5469
5473
5470 By default, servers will ignore `--pushvars`. To enable it add the
5474 By default, servers will ignore `--pushvars`. To enable it add the
5471 following to your configuration file::
5475 following to your configuration file::
5472
5476
5473 [push]
5477 [push]
5474 pushvars.server = true
5478 pushvars.server = true
5475
5479
5476 Returns 0 if push was successful, 1 if nothing to push.
5480 Returns 0 if push was successful, 1 if nothing to push.
5477 """
5481 """
5478
5482
5479 opts = pycompat.byteskwargs(opts)
5483 opts = pycompat.byteskwargs(opts)
5480
5484
5481 if opts.get(b'all_bookmarks'):
5485 if opts.get(b'all_bookmarks'):
5482 cmdutil.check_incompatible_arguments(
5486 cmdutil.check_incompatible_arguments(
5483 opts, b'all_bookmarks', [b'bookmark', b'rev'],
5487 opts, b'all_bookmarks', [b'bookmark', b'rev'],
5484 )
5488 )
5485 opts[b'bookmark'] = list(repo._bookmarks)
5489 opts[b'bookmark'] = list(repo._bookmarks)
5486
5490
5487 if opts.get(b'bookmark'):
5491 if opts.get(b'bookmark'):
5488 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5492 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5489 for b in opts[b'bookmark']:
5493 for b in opts[b'bookmark']:
5490 # translate -B options to -r so changesets get pushed
5494 # translate -B options to -r so changesets get pushed
5491 b = repo._bookmarks.expandname(b)
5495 b = repo._bookmarks.expandname(b)
5492 if b in repo._bookmarks:
5496 if b in repo._bookmarks:
5493 opts.setdefault(b'rev', []).append(b)
5497 opts.setdefault(b'rev', []).append(b)
5494 else:
5498 else:
5495 # if we try to push a deleted bookmark, translate it to null
5499 # if we try to push a deleted bookmark, translate it to null
5496 # this lets simultaneous -r, -b options continue working
5500 # this lets simultaneous -r, -b options continue working
5497 opts.setdefault(b'rev', []).append(b"null")
5501 opts.setdefault(b'rev', []).append(b"null")
5498
5502
5499 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5503 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5500 if not path:
5504 if not path:
5501 raise error.Abort(
5505 raise error.Abort(
5502 _(b'default repository not configured!'),
5506 _(b'default repository not configured!'),
5503 hint=_(b"see 'hg help config.paths'"),
5507 hint=_(b"see 'hg help config.paths'"),
5504 )
5508 )
5505 dest = path.pushloc or path.loc
5509 dest = path.pushloc or path.loc
5506 branches = (path.branch, opts.get(b'branch') or [])
5510 branches = (path.branch, opts.get(b'branch') or [])
5507 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5511 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5508 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5512 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5509 other = hg.peer(repo, opts, dest)
5513 other = hg.peer(repo, opts, dest)
5510
5514
5511 if revs:
5515 if revs:
5512 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5516 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5513 if not revs:
5517 if not revs:
5514 raise error.Abort(
5518 raise error.Abort(
5515 _(b"specified revisions evaluate to an empty set"),
5519 _(b"specified revisions evaluate to an empty set"),
5516 hint=_(b"use different revision arguments"),
5520 hint=_(b"use different revision arguments"),
5517 )
5521 )
5518 elif path.pushrev:
5522 elif path.pushrev:
5519 # It doesn't make any sense to specify ancestor revisions. So limit
5523 # It doesn't make any sense to specify ancestor revisions. So limit
5520 # to DAG heads to make discovery simpler.
5524 # to DAG heads to make discovery simpler.
5521 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5525 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5522 revs = scmutil.revrange(repo, [expr])
5526 revs = scmutil.revrange(repo, [expr])
5523 revs = [repo[rev].node() for rev in revs]
5527 revs = [repo[rev].node() for rev in revs]
5524 if not revs:
5528 if not revs:
5525 raise error.Abort(
5529 raise error.Abort(
5526 _(b'default push revset for path evaluates to an empty set')
5530 _(b'default push revset for path evaluates to an empty set')
5527 )
5531 )
5528 elif ui.configbool(b'commands', b'push.require-revs'):
5532 elif ui.configbool(b'commands', b'push.require-revs'):
5529 raise error.Abort(
5533 raise error.Abort(
5530 _(b'no revisions specified to push'),
5534 _(b'no revisions specified to push'),
5531 hint=_(b'did you mean "hg push -r ."?'),
5535 hint=_(b'did you mean "hg push -r ."?'),
5532 )
5536 )
5533
5537
5534 repo._subtoppath = dest
5538 repo._subtoppath = dest
5535 try:
5539 try:
5536 # push subrepos depth-first for coherent ordering
5540 # push subrepos depth-first for coherent ordering
5537 c = repo[b'.']
5541 c = repo[b'.']
5538 subs = c.substate # only repos that are committed
5542 subs = c.substate # only repos that are committed
5539 for s in sorted(subs):
5543 for s in sorted(subs):
5540 result = c.sub(s).push(opts)
5544 result = c.sub(s).push(opts)
5541 if result == 0:
5545 if result == 0:
5542 return not result
5546 return not result
5543 finally:
5547 finally:
5544 del repo._subtoppath
5548 del repo._subtoppath
5545
5549
5546 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5550 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5547 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5551 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5548
5552
5549 pushop = exchange.push(
5553 pushop = exchange.push(
5550 repo,
5554 repo,
5551 other,
5555 other,
5552 opts.get(b'force'),
5556 opts.get(b'force'),
5553 revs=revs,
5557 revs=revs,
5554 newbranch=opts.get(b'new_branch'),
5558 newbranch=opts.get(b'new_branch'),
5555 bookmarks=opts.get(b'bookmark', ()),
5559 bookmarks=opts.get(b'bookmark', ()),
5556 publish=opts.get(b'publish'),
5560 publish=opts.get(b'publish'),
5557 opargs=opargs,
5561 opargs=opargs,
5558 )
5562 )
5559
5563
5560 result = not pushop.cgresult
5564 result = not pushop.cgresult
5561
5565
5562 if pushop.bkresult is not None:
5566 if pushop.bkresult is not None:
5563 if pushop.bkresult == 2:
5567 if pushop.bkresult == 2:
5564 result = 2
5568 result = 2
5565 elif not result and pushop.bkresult:
5569 elif not result and pushop.bkresult:
5566 result = 2
5570 result = 2
5567
5571
5568 return result
5572 return result
5569
5573
5570
5574
5571 @command(
5575 @command(
5572 b'recover',
5576 b'recover',
5573 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5577 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5574 helpcategory=command.CATEGORY_MAINTENANCE,
5578 helpcategory=command.CATEGORY_MAINTENANCE,
5575 )
5579 )
5576 def recover(ui, repo, **opts):
5580 def recover(ui, repo, **opts):
5577 """roll back an interrupted transaction
5581 """roll back an interrupted transaction
5578
5582
5579 Recover from an interrupted commit or pull.
5583 Recover from an interrupted commit or pull.
5580
5584
5581 This command tries to fix the repository status after an
5585 This command tries to fix the repository status after an
5582 interrupted operation. It should only be necessary when Mercurial
5586 interrupted operation. It should only be necessary when Mercurial
5583 suggests it.
5587 suggests it.
5584
5588
5585 Returns 0 if successful, 1 if nothing to recover or verify fails.
5589 Returns 0 if successful, 1 if nothing to recover or verify fails.
5586 """
5590 """
5587 ret = repo.recover()
5591 ret = repo.recover()
5588 if ret:
5592 if ret:
5589 if opts['verify']:
5593 if opts['verify']:
5590 return hg.verify(repo)
5594 return hg.verify(repo)
5591 else:
5595 else:
5592 msg = _(
5596 msg = _(
5593 b"(verify step skipped, run `hg verify` to check your "
5597 b"(verify step skipped, run `hg verify` to check your "
5594 b"repository content)\n"
5598 b"repository content)\n"
5595 )
5599 )
5596 ui.warn(msg)
5600 ui.warn(msg)
5597 return 0
5601 return 0
5598 return 1
5602 return 1
5599
5603
5600
5604
5601 @command(
5605 @command(
5602 b'remove|rm',
5606 b'remove|rm',
5603 [
5607 [
5604 (b'A', b'after', None, _(b'record delete for missing files')),
5608 (b'A', b'after', None, _(b'record delete for missing files')),
5605 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5609 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5606 ]
5610 ]
5607 + subrepoopts
5611 + subrepoopts
5608 + walkopts
5612 + walkopts
5609 + dryrunopts,
5613 + dryrunopts,
5610 _(b'[OPTION]... FILE...'),
5614 _(b'[OPTION]... FILE...'),
5611 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5615 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5612 helpbasic=True,
5616 helpbasic=True,
5613 inferrepo=True,
5617 inferrepo=True,
5614 )
5618 )
5615 def remove(ui, repo, *pats, **opts):
5619 def remove(ui, repo, *pats, **opts):
5616 """remove the specified files on the next commit
5620 """remove the specified files on the next commit
5617
5621
5618 Schedule the indicated files for removal from the current branch.
5622 Schedule the indicated files for removal from the current branch.
5619
5623
5620 This command schedules the files to be removed at the next commit.
5624 This command schedules the files to be removed at the next commit.
5621 To undo a remove before that, see :hg:`revert`. To undo added
5625 To undo a remove before that, see :hg:`revert`. To undo added
5622 files, see :hg:`forget`.
5626 files, see :hg:`forget`.
5623
5627
5624 .. container:: verbose
5628 .. container:: verbose
5625
5629
5626 -A/--after can be used to remove only files that have already
5630 -A/--after can be used to remove only files that have already
5627 been deleted, -f/--force can be used to force deletion, and -Af
5631 been deleted, -f/--force can be used to force deletion, and -Af
5628 can be used to remove files from the next revision without
5632 can be used to remove files from the next revision without
5629 deleting them from the working directory.
5633 deleting them from the working directory.
5630
5634
5631 The following table details the behavior of remove for different
5635 The following table details the behavior of remove for different
5632 file states (columns) and option combinations (rows). The file
5636 file states (columns) and option combinations (rows). The file
5633 states are Added [A], Clean [C], Modified [M] and Missing [!]
5637 states are Added [A], Clean [C], Modified [M] and Missing [!]
5634 (as reported by :hg:`status`). The actions are Warn, Remove
5638 (as reported by :hg:`status`). The actions are Warn, Remove
5635 (from branch) and Delete (from disk):
5639 (from branch) and Delete (from disk):
5636
5640
5637 ========= == == == ==
5641 ========= == == == ==
5638 opt/state A C M !
5642 opt/state A C M !
5639 ========= == == == ==
5643 ========= == == == ==
5640 none W RD W R
5644 none W RD W R
5641 -f R RD RD R
5645 -f R RD RD R
5642 -A W W W R
5646 -A W W W R
5643 -Af R R R R
5647 -Af R R R R
5644 ========= == == == ==
5648 ========= == == == ==
5645
5649
5646 .. note::
5650 .. note::
5647
5651
5648 :hg:`remove` never deletes files in Added [A] state from the
5652 :hg:`remove` never deletes files in Added [A] state from the
5649 working directory, not even if ``--force`` is specified.
5653 working directory, not even if ``--force`` is specified.
5650
5654
5651 Returns 0 on success, 1 if any warnings encountered.
5655 Returns 0 on success, 1 if any warnings encountered.
5652 """
5656 """
5653
5657
5654 opts = pycompat.byteskwargs(opts)
5658 opts = pycompat.byteskwargs(opts)
5655 after, force = opts.get(b'after'), opts.get(b'force')
5659 after, force = opts.get(b'after'), opts.get(b'force')
5656 dryrun = opts.get(b'dry_run')
5660 dryrun = opts.get(b'dry_run')
5657 if not pats and not after:
5661 if not pats and not after:
5658 raise error.Abort(_(b'no files specified'))
5662 raise error.Abort(_(b'no files specified'))
5659
5663
5660 m = scmutil.match(repo[None], pats, opts)
5664 m = scmutil.match(repo[None], pats, opts)
5661 subrepos = opts.get(b'subrepos')
5665 subrepos = opts.get(b'subrepos')
5662 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5666 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5663 return cmdutil.remove(
5667 return cmdutil.remove(
5664 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5668 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5665 )
5669 )
5666
5670
5667
5671
5668 @command(
5672 @command(
5669 b'rename|move|mv',
5673 b'rename|move|mv',
5670 [
5674 [
5671 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5675 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5672 (
5676 (
5673 b'',
5677 b'',
5674 b'at-rev',
5678 b'at-rev',
5675 b'',
5679 b'',
5676 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5680 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5677 _(b'REV'),
5681 _(b'REV'),
5678 ),
5682 ),
5679 (
5683 (
5680 b'f',
5684 b'f',
5681 b'force',
5685 b'force',
5682 None,
5686 None,
5683 _(b'forcibly move over an existing managed file'),
5687 _(b'forcibly move over an existing managed file'),
5684 ),
5688 ),
5685 ]
5689 ]
5686 + walkopts
5690 + walkopts
5687 + dryrunopts,
5691 + dryrunopts,
5688 _(b'[OPTION]... SOURCE... DEST'),
5692 _(b'[OPTION]... SOURCE... DEST'),
5689 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5693 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5690 )
5694 )
5691 def rename(ui, repo, *pats, **opts):
5695 def rename(ui, repo, *pats, **opts):
5692 """rename files; equivalent of copy + remove
5696 """rename files; equivalent of copy + remove
5693
5697
5694 Mark dest as copies of sources; mark sources for deletion. If dest
5698 Mark dest as copies of sources; mark sources for deletion. If dest
5695 is a directory, copies are put in that directory. If dest is a
5699 is a directory, copies are put in that directory. If dest is a
5696 file, there can only be one source.
5700 file, there can only be one source.
5697
5701
5698 By default, this command copies the contents of files as they
5702 By default, this command copies the contents of files as they
5699 exist in the working directory. If invoked with -A/--after, the
5703 exist in the working directory. If invoked with -A/--after, the
5700 operation is recorded, but no copying is performed.
5704 operation is recorded, but no copying is performed.
5701
5705
5702 This command takes effect at the next commit. To undo a rename
5706 This command takes effect at the next commit. To undo a rename
5703 before that, see :hg:`revert`.
5707 before that, see :hg:`revert`.
5704
5708
5705 Returns 0 on success, 1 if errors are encountered.
5709 Returns 0 on success, 1 if errors are encountered.
5706 """
5710 """
5707 opts = pycompat.byteskwargs(opts)
5711 opts = pycompat.byteskwargs(opts)
5708 with repo.wlock():
5712 with repo.wlock():
5709 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5713 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5710
5714
5711
5715
5712 @command(
5716 @command(
5713 b'resolve',
5717 b'resolve',
5714 [
5718 [
5715 (b'a', b'all', None, _(b'select all unresolved files')),
5719 (b'a', b'all', None, _(b'select all unresolved files')),
5716 (b'l', b'list', None, _(b'list state of files needing merge')),
5720 (b'l', b'list', None, _(b'list state of files needing merge')),
5717 (b'm', b'mark', None, _(b'mark files as resolved')),
5721 (b'm', b'mark', None, _(b'mark files as resolved')),
5718 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5722 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5719 (b'n', b'no-status', None, _(b'hide status prefix')),
5723 (b'n', b'no-status', None, _(b'hide status prefix')),
5720 (b'', b're-merge', None, _(b're-merge files')),
5724 (b'', b're-merge', None, _(b're-merge files')),
5721 ]
5725 ]
5722 + mergetoolopts
5726 + mergetoolopts
5723 + walkopts
5727 + walkopts
5724 + formatteropts,
5728 + formatteropts,
5725 _(b'[OPTION]... [FILE]...'),
5729 _(b'[OPTION]... [FILE]...'),
5726 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5730 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5727 inferrepo=True,
5731 inferrepo=True,
5728 )
5732 )
5729 def resolve(ui, repo, *pats, **opts):
5733 def resolve(ui, repo, *pats, **opts):
5730 """redo merges or set/view the merge status of files
5734 """redo merges or set/view the merge status of files
5731
5735
5732 Merges with unresolved conflicts are often the result of
5736 Merges with unresolved conflicts are often the result of
5733 non-interactive merging using the ``internal:merge`` configuration
5737 non-interactive merging using the ``internal:merge`` configuration
5734 setting, or a command-line merge tool like ``diff3``. The resolve
5738 setting, or a command-line merge tool like ``diff3``. The resolve
5735 command is used to manage the files involved in a merge, after
5739 command is used to manage the files involved in a merge, after
5736 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5740 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5737 working directory must have two parents). See :hg:`help
5741 working directory must have two parents). See :hg:`help
5738 merge-tools` for information on configuring merge tools.
5742 merge-tools` for information on configuring merge tools.
5739
5743
5740 The resolve command can be used in the following ways:
5744 The resolve command can be used in the following ways:
5741
5745
5742 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5746 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5743 the specified files, discarding any previous merge attempts. Re-merging
5747 the specified files, discarding any previous merge attempts. Re-merging
5744 is not performed for files already marked as resolved. Use ``--all/-a``
5748 is not performed for files already marked as resolved. Use ``--all/-a``
5745 to select all unresolved files. ``--tool`` can be used to specify
5749 to select all unresolved files. ``--tool`` can be used to specify
5746 the merge tool used for the given files. It overrides the HGMERGE
5750 the merge tool used for the given files. It overrides the HGMERGE
5747 environment variable and your configuration files. Previous file
5751 environment variable and your configuration files. Previous file
5748 contents are saved with a ``.orig`` suffix.
5752 contents are saved with a ``.orig`` suffix.
5749
5753
5750 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5754 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5751 (e.g. after having manually fixed-up the files). The default is
5755 (e.g. after having manually fixed-up the files). The default is
5752 to mark all unresolved files.
5756 to mark all unresolved files.
5753
5757
5754 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5758 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5755 default is to mark all resolved files.
5759 default is to mark all resolved files.
5756
5760
5757 - :hg:`resolve -l`: list files which had or still have conflicts.
5761 - :hg:`resolve -l`: list files which had or still have conflicts.
5758 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5762 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5759 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5763 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5760 the list. See :hg:`help filesets` for details.
5764 the list. See :hg:`help filesets` for details.
5761
5765
5762 .. note::
5766 .. note::
5763
5767
5764 Mercurial will not let you commit files with unresolved merge
5768 Mercurial will not let you commit files with unresolved merge
5765 conflicts. You must use :hg:`resolve -m ...` before you can
5769 conflicts. You must use :hg:`resolve -m ...` before you can
5766 commit after a conflicting merge.
5770 commit after a conflicting merge.
5767
5771
5768 .. container:: verbose
5772 .. container:: verbose
5769
5773
5770 Template:
5774 Template:
5771
5775
5772 The following keywords are supported in addition to the common template
5776 The following keywords are supported in addition to the common template
5773 keywords and functions. See also :hg:`help templates`.
5777 keywords and functions. See also :hg:`help templates`.
5774
5778
5775 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5779 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5776 :path: String. Repository-absolute path of the file.
5780 :path: String. Repository-absolute path of the file.
5777
5781
5778 Returns 0 on success, 1 if any files fail a resolve attempt.
5782 Returns 0 on success, 1 if any files fail a resolve attempt.
5779 """
5783 """
5780
5784
5781 opts = pycompat.byteskwargs(opts)
5785 opts = pycompat.byteskwargs(opts)
5782 confirm = ui.configbool(b'commands', b'resolve.confirm')
5786 confirm = ui.configbool(b'commands', b'resolve.confirm')
5783 flaglist = b'all mark unmark list no_status re_merge'.split()
5787 flaglist = b'all mark unmark list no_status re_merge'.split()
5784 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5788 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5785
5789
5786 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5790 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5787 if actioncount > 1:
5791 if actioncount > 1:
5788 raise error.Abort(_(b"too many actions specified"))
5792 raise error.Abort(_(b"too many actions specified"))
5789 elif actioncount == 0 and ui.configbool(
5793 elif actioncount == 0 and ui.configbool(
5790 b'commands', b'resolve.explicit-re-merge'
5794 b'commands', b'resolve.explicit-re-merge'
5791 ):
5795 ):
5792 hint = _(b'use --mark, --unmark, --list or --re-merge')
5796 hint = _(b'use --mark, --unmark, --list or --re-merge')
5793 raise error.Abort(_(b'no action specified'), hint=hint)
5797 raise error.Abort(_(b'no action specified'), hint=hint)
5794 if pats and all:
5798 if pats and all:
5795 raise error.Abort(_(b"can't specify --all and patterns"))
5799 raise error.Abort(_(b"can't specify --all and patterns"))
5796 if not (all or pats or show or mark or unmark):
5800 if not (all or pats or show or mark or unmark):
5797 raise error.Abort(
5801 raise error.Abort(
5798 _(b'no files or directories specified'),
5802 _(b'no files or directories specified'),
5799 hint=b'use --all to re-merge all unresolved files',
5803 hint=b'use --all to re-merge all unresolved files',
5800 )
5804 )
5801
5805
5802 if confirm:
5806 if confirm:
5803 if all:
5807 if all:
5804 if ui.promptchoice(
5808 if ui.promptchoice(
5805 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5809 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5806 ):
5810 ):
5807 raise error.Abort(_(b'user quit'))
5811 raise error.Abort(_(b'user quit'))
5808 if mark and not pats:
5812 if mark and not pats:
5809 if ui.promptchoice(
5813 if ui.promptchoice(
5810 _(
5814 _(
5811 b'mark all unresolved files as resolved (yn)?'
5815 b'mark all unresolved files as resolved (yn)?'
5812 b'$$ &Yes $$ &No'
5816 b'$$ &Yes $$ &No'
5813 )
5817 )
5814 ):
5818 ):
5815 raise error.Abort(_(b'user quit'))
5819 raise error.Abort(_(b'user quit'))
5816 if unmark and not pats:
5820 if unmark and not pats:
5817 if ui.promptchoice(
5821 if ui.promptchoice(
5818 _(
5822 _(
5819 b'mark all resolved files as unresolved (yn)?'
5823 b'mark all resolved files as unresolved (yn)?'
5820 b'$$ &Yes $$ &No'
5824 b'$$ &Yes $$ &No'
5821 )
5825 )
5822 ):
5826 ):
5823 raise error.Abort(_(b'user quit'))
5827 raise error.Abort(_(b'user quit'))
5824
5828
5825 uipathfn = scmutil.getuipathfn(repo)
5829 uipathfn = scmutil.getuipathfn(repo)
5826
5830
5827 if show:
5831 if show:
5828 ui.pager(b'resolve')
5832 ui.pager(b'resolve')
5829 fm = ui.formatter(b'resolve', opts)
5833 fm = ui.formatter(b'resolve', opts)
5830 ms = mergestatemod.mergestate.read(repo)
5834 ms = mergestatemod.mergestate.read(repo)
5831 wctx = repo[None]
5835 wctx = repo[None]
5832 m = scmutil.match(wctx, pats, opts)
5836 m = scmutil.match(wctx, pats, opts)
5833
5837
5834 # Labels and keys based on merge state. Unresolved path conflicts show
5838 # Labels and keys based on merge state. Unresolved path conflicts show
5835 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5839 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5836 # resolved conflicts.
5840 # resolved conflicts.
5837 mergestateinfo = {
5841 mergestateinfo = {
5838 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5842 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5839 b'resolve.unresolved',
5843 b'resolve.unresolved',
5840 b'U',
5844 b'U',
5841 ),
5845 ),
5842 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5846 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5843 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5847 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5844 b'resolve.unresolved',
5848 b'resolve.unresolved',
5845 b'P',
5849 b'P',
5846 ),
5850 ),
5847 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5851 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5848 b'resolve.resolved',
5852 b'resolve.resolved',
5849 b'R',
5853 b'R',
5850 ),
5854 ),
5851 }
5855 }
5852
5856
5853 for f in ms:
5857 for f in ms:
5854 if not m(f):
5858 if not m(f):
5855 continue
5859 continue
5856
5860
5857 label, key = mergestateinfo[ms[f]]
5861 label, key = mergestateinfo[ms[f]]
5858 fm.startitem()
5862 fm.startitem()
5859 fm.context(ctx=wctx)
5863 fm.context(ctx=wctx)
5860 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5864 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5861 fm.data(path=f)
5865 fm.data(path=f)
5862 fm.plain(b'%s\n' % uipathfn(f), label=label)
5866 fm.plain(b'%s\n' % uipathfn(f), label=label)
5863 fm.end()
5867 fm.end()
5864 return 0
5868 return 0
5865
5869
5866 with repo.wlock():
5870 with repo.wlock():
5867 ms = mergestatemod.mergestate.read(repo)
5871 ms = mergestatemod.mergestate.read(repo)
5868
5872
5869 if not (ms.active() or repo.dirstate.p2() != nullid):
5873 if not (ms.active() or repo.dirstate.p2() != nullid):
5870 raise error.Abort(
5874 raise error.Abort(
5871 _(b'resolve command not applicable when not merging')
5875 _(b'resolve command not applicable when not merging')
5872 )
5876 )
5873
5877
5874 wctx = repo[None]
5878 wctx = repo[None]
5875 m = scmutil.match(wctx, pats, opts)
5879 m = scmutil.match(wctx, pats, opts)
5876 ret = 0
5880 ret = 0
5877 didwork = False
5881 didwork = False
5878
5882
5879 tocomplete = []
5883 tocomplete = []
5880 hasconflictmarkers = []
5884 hasconflictmarkers = []
5881 if mark:
5885 if mark:
5882 markcheck = ui.config(b'commands', b'resolve.mark-check')
5886 markcheck = ui.config(b'commands', b'resolve.mark-check')
5883 if markcheck not in [b'warn', b'abort']:
5887 if markcheck not in [b'warn', b'abort']:
5884 # Treat all invalid / unrecognized values as 'none'.
5888 # Treat all invalid / unrecognized values as 'none'.
5885 markcheck = False
5889 markcheck = False
5886 for f in ms:
5890 for f in ms:
5887 if not m(f):
5891 if not m(f):
5888 continue
5892 continue
5889
5893
5890 didwork = True
5894 didwork = True
5891
5895
5892 # path conflicts must be resolved manually
5896 # path conflicts must be resolved manually
5893 if ms[f] in (
5897 if ms[f] in (
5894 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5898 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5895 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5899 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5896 ):
5900 ):
5897 if mark:
5901 if mark:
5898 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5902 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5899 elif unmark:
5903 elif unmark:
5900 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5904 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5901 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5905 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5902 ui.warn(
5906 ui.warn(
5903 _(b'%s: path conflict must be resolved manually\n')
5907 _(b'%s: path conflict must be resolved manually\n')
5904 % uipathfn(f)
5908 % uipathfn(f)
5905 )
5909 )
5906 continue
5910 continue
5907
5911
5908 if mark:
5912 if mark:
5909 if markcheck:
5913 if markcheck:
5910 fdata = repo.wvfs.tryread(f)
5914 fdata = repo.wvfs.tryread(f)
5911 if (
5915 if (
5912 filemerge.hasconflictmarkers(fdata)
5916 filemerge.hasconflictmarkers(fdata)
5913 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5917 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5914 ):
5918 ):
5915 hasconflictmarkers.append(f)
5919 hasconflictmarkers.append(f)
5916 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5920 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5917 elif unmark:
5921 elif unmark:
5918 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5922 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5919 else:
5923 else:
5920 # backup pre-resolve (merge uses .orig for its own purposes)
5924 # backup pre-resolve (merge uses .orig for its own purposes)
5921 a = repo.wjoin(f)
5925 a = repo.wjoin(f)
5922 try:
5926 try:
5923 util.copyfile(a, a + b".resolve")
5927 util.copyfile(a, a + b".resolve")
5924 except (IOError, OSError) as inst:
5928 except (IOError, OSError) as inst:
5925 if inst.errno != errno.ENOENT:
5929 if inst.errno != errno.ENOENT:
5926 raise
5930 raise
5927
5931
5928 try:
5932 try:
5929 # preresolve file
5933 # preresolve file
5930 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5934 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5931 with ui.configoverride(overrides, b'resolve'):
5935 with ui.configoverride(overrides, b'resolve'):
5932 complete, r = ms.preresolve(f, wctx)
5936 complete, r = ms.preresolve(f, wctx)
5933 if not complete:
5937 if not complete:
5934 tocomplete.append(f)
5938 tocomplete.append(f)
5935 elif r:
5939 elif r:
5936 ret = 1
5940 ret = 1
5937 finally:
5941 finally:
5938 ms.commit()
5942 ms.commit()
5939
5943
5940 # replace filemerge's .orig file with our resolve file, but only
5944 # replace filemerge's .orig file with our resolve file, but only
5941 # for merges that are complete
5945 # for merges that are complete
5942 if complete:
5946 if complete:
5943 try:
5947 try:
5944 util.rename(
5948 util.rename(
5945 a + b".resolve", scmutil.backuppath(ui, repo, f)
5949 a + b".resolve", scmutil.backuppath(ui, repo, f)
5946 )
5950 )
5947 except OSError as inst:
5951 except OSError as inst:
5948 if inst.errno != errno.ENOENT:
5952 if inst.errno != errno.ENOENT:
5949 raise
5953 raise
5950
5954
5951 if hasconflictmarkers:
5955 if hasconflictmarkers:
5952 ui.warn(
5956 ui.warn(
5953 _(
5957 _(
5954 b'warning: the following files still have conflict '
5958 b'warning: the following files still have conflict '
5955 b'markers:\n'
5959 b'markers:\n'
5956 )
5960 )
5957 + b''.join(
5961 + b''.join(
5958 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
5962 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
5959 )
5963 )
5960 )
5964 )
5961 if markcheck == b'abort' and not all and not pats:
5965 if markcheck == b'abort' and not all and not pats:
5962 raise error.Abort(
5966 raise error.Abort(
5963 _(b'conflict markers detected'),
5967 _(b'conflict markers detected'),
5964 hint=_(b'use --all to mark anyway'),
5968 hint=_(b'use --all to mark anyway'),
5965 )
5969 )
5966
5970
5967 for f in tocomplete:
5971 for f in tocomplete:
5968 try:
5972 try:
5969 # resolve file
5973 # resolve file
5970 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5974 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5971 with ui.configoverride(overrides, b'resolve'):
5975 with ui.configoverride(overrides, b'resolve'):
5972 r = ms.resolve(f, wctx)
5976 r = ms.resolve(f, wctx)
5973 if r:
5977 if r:
5974 ret = 1
5978 ret = 1
5975 finally:
5979 finally:
5976 ms.commit()
5980 ms.commit()
5977
5981
5978 # replace filemerge's .orig file with our resolve file
5982 # replace filemerge's .orig file with our resolve file
5979 a = repo.wjoin(f)
5983 a = repo.wjoin(f)
5980 try:
5984 try:
5981 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
5985 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
5982 except OSError as inst:
5986 except OSError as inst:
5983 if inst.errno != errno.ENOENT:
5987 if inst.errno != errno.ENOENT:
5984 raise
5988 raise
5985
5989
5986 ms.commit()
5990 ms.commit()
5987 branchmerge = repo.dirstate.p2() != nullid
5991 branchmerge = repo.dirstate.p2() != nullid
5988 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
5992 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
5989
5993
5990 if not didwork and pats:
5994 if not didwork and pats:
5991 hint = None
5995 hint = None
5992 if not any([p for p in pats if p.find(b':') >= 0]):
5996 if not any([p for p in pats if p.find(b':') >= 0]):
5993 pats = [b'path:%s' % p for p in pats]
5997 pats = [b'path:%s' % p for p in pats]
5994 m = scmutil.match(wctx, pats, opts)
5998 m = scmutil.match(wctx, pats, opts)
5995 for f in ms:
5999 for f in ms:
5996 if not m(f):
6000 if not m(f):
5997 continue
6001 continue
5998
6002
5999 def flag(o):
6003 def flag(o):
6000 if o == b're_merge':
6004 if o == b're_merge':
6001 return b'--re-merge '
6005 return b'--re-merge '
6002 return b'-%s ' % o[0:1]
6006 return b'-%s ' % o[0:1]
6003
6007
6004 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6008 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6005 hint = _(b"(try: hg resolve %s%s)\n") % (
6009 hint = _(b"(try: hg resolve %s%s)\n") % (
6006 flags,
6010 flags,
6007 b' '.join(pats),
6011 b' '.join(pats),
6008 )
6012 )
6009 break
6013 break
6010 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6014 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6011 if hint:
6015 if hint:
6012 ui.warn(hint)
6016 ui.warn(hint)
6013
6017
6014 unresolvedf = list(ms.unresolved())
6018 unresolvedf = list(ms.unresolved())
6015 if not unresolvedf:
6019 if not unresolvedf:
6016 ui.status(_(b'(no more unresolved files)\n'))
6020 ui.status(_(b'(no more unresolved files)\n'))
6017 cmdutil.checkafterresolved(repo)
6021 cmdutil.checkafterresolved(repo)
6018
6022
6019 return ret
6023 return ret
6020
6024
6021
6025
6022 @command(
6026 @command(
6023 b'revert',
6027 b'revert',
6024 [
6028 [
6025 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6029 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6026 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6030 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6027 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6031 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6028 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6032 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6029 (b'i', b'interactive', None, _(b'interactively select the changes')),
6033 (b'i', b'interactive', None, _(b'interactively select the changes')),
6030 ]
6034 ]
6031 + walkopts
6035 + walkopts
6032 + dryrunopts,
6036 + dryrunopts,
6033 _(b'[OPTION]... [-r REV] [NAME]...'),
6037 _(b'[OPTION]... [-r REV] [NAME]...'),
6034 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6038 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6035 )
6039 )
6036 def revert(ui, repo, *pats, **opts):
6040 def revert(ui, repo, *pats, **opts):
6037 """restore files to their checkout state
6041 """restore files to their checkout state
6038
6042
6039 .. note::
6043 .. note::
6040
6044
6041 To check out earlier revisions, you should use :hg:`update REV`.
6045 To check out earlier revisions, you should use :hg:`update REV`.
6042 To cancel an uncommitted merge (and lose your changes),
6046 To cancel an uncommitted merge (and lose your changes),
6043 use :hg:`merge --abort`.
6047 use :hg:`merge --abort`.
6044
6048
6045 With no revision specified, revert the specified files or directories
6049 With no revision specified, revert the specified files or directories
6046 to the contents they had in the parent of the working directory.
6050 to the contents they had in the parent of the working directory.
6047 This restores the contents of files to an unmodified
6051 This restores the contents of files to an unmodified
6048 state and unschedules adds, removes, copies, and renames. If the
6052 state and unschedules adds, removes, copies, and renames. If the
6049 working directory has two parents, you must explicitly specify a
6053 working directory has two parents, you must explicitly specify a
6050 revision.
6054 revision.
6051
6055
6052 Using the -r/--rev or -d/--date options, revert the given files or
6056 Using the -r/--rev or -d/--date options, revert the given files or
6053 directories to their states as of a specific revision. Because
6057 directories to their states as of a specific revision. Because
6054 revert does not change the working directory parents, this will
6058 revert does not change the working directory parents, this will
6055 cause these files to appear modified. This can be helpful to "back
6059 cause these files to appear modified. This can be helpful to "back
6056 out" some or all of an earlier change. See :hg:`backout` for a
6060 out" some or all of an earlier change. See :hg:`backout` for a
6057 related method.
6061 related method.
6058
6062
6059 Modified files are saved with a .orig suffix before reverting.
6063 Modified files are saved with a .orig suffix before reverting.
6060 To disable these backups, use --no-backup. It is possible to store
6064 To disable these backups, use --no-backup. It is possible to store
6061 the backup files in a custom directory relative to the root of the
6065 the backup files in a custom directory relative to the root of the
6062 repository by setting the ``ui.origbackuppath`` configuration
6066 repository by setting the ``ui.origbackuppath`` configuration
6063 option.
6067 option.
6064
6068
6065 See :hg:`help dates` for a list of formats valid for -d/--date.
6069 See :hg:`help dates` for a list of formats valid for -d/--date.
6066
6070
6067 See :hg:`help backout` for a way to reverse the effect of an
6071 See :hg:`help backout` for a way to reverse the effect of an
6068 earlier changeset.
6072 earlier changeset.
6069
6073
6070 Returns 0 on success.
6074 Returns 0 on success.
6071 """
6075 """
6072
6076
6073 opts = pycompat.byteskwargs(opts)
6077 opts = pycompat.byteskwargs(opts)
6074 if opts.get(b"date"):
6078 if opts.get(b"date"):
6075 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6079 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6076 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6080 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6077
6081
6078 parent, p2 = repo.dirstate.parents()
6082 parent, p2 = repo.dirstate.parents()
6079 if not opts.get(b'rev') and p2 != nullid:
6083 if not opts.get(b'rev') and p2 != nullid:
6080 # revert after merge is a trap for new users (issue2915)
6084 # revert after merge is a trap for new users (issue2915)
6081 raise error.Abort(
6085 raise error.Abort(
6082 _(b'uncommitted merge with no revision specified'),
6086 _(b'uncommitted merge with no revision specified'),
6083 hint=_(b"use 'hg update' or see 'hg help revert'"),
6087 hint=_(b"use 'hg update' or see 'hg help revert'"),
6084 )
6088 )
6085
6089
6086 rev = opts.get(b'rev')
6090 rev = opts.get(b'rev')
6087 if rev:
6091 if rev:
6088 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6092 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6089 ctx = scmutil.revsingle(repo, rev)
6093 ctx = scmutil.revsingle(repo, rev)
6090
6094
6091 if not (
6095 if not (
6092 pats
6096 pats
6093 or opts.get(b'include')
6097 or opts.get(b'include')
6094 or opts.get(b'exclude')
6098 or opts.get(b'exclude')
6095 or opts.get(b'all')
6099 or opts.get(b'all')
6096 or opts.get(b'interactive')
6100 or opts.get(b'interactive')
6097 ):
6101 ):
6098 msg = _(b"no files or directories specified")
6102 msg = _(b"no files or directories specified")
6099 if p2 != nullid:
6103 if p2 != nullid:
6100 hint = _(
6104 hint = _(
6101 b"uncommitted merge, use --all to discard all changes,"
6105 b"uncommitted merge, use --all to discard all changes,"
6102 b" or 'hg update -C .' to abort the merge"
6106 b" or 'hg update -C .' to abort the merge"
6103 )
6107 )
6104 raise error.Abort(msg, hint=hint)
6108 raise error.Abort(msg, hint=hint)
6105 dirty = any(repo.status())
6109 dirty = any(repo.status())
6106 node = ctx.node()
6110 node = ctx.node()
6107 if node != parent:
6111 if node != parent:
6108 if dirty:
6112 if dirty:
6109 hint = (
6113 hint = (
6110 _(
6114 _(
6111 b"uncommitted changes, use --all to discard all"
6115 b"uncommitted changes, use --all to discard all"
6112 b" changes, or 'hg update %d' to update"
6116 b" changes, or 'hg update %d' to update"
6113 )
6117 )
6114 % ctx.rev()
6118 % ctx.rev()
6115 )
6119 )
6116 else:
6120 else:
6117 hint = (
6121 hint = (
6118 _(
6122 _(
6119 b"use --all to revert all files,"
6123 b"use --all to revert all files,"
6120 b" or 'hg update %d' to update"
6124 b" or 'hg update %d' to update"
6121 )
6125 )
6122 % ctx.rev()
6126 % ctx.rev()
6123 )
6127 )
6124 elif dirty:
6128 elif dirty:
6125 hint = _(b"uncommitted changes, use --all to discard all changes")
6129 hint = _(b"uncommitted changes, use --all to discard all changes")
6126 else:
6130 else:
6127 hint = _(b"use --all to revert all files")
6131 hint = _(b"use --all to revert all files")
6128 raise error.Abort(msg, hint=hint)
6132 raise error.Abort(msg, hint=hint)
6129
6133
6130 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6134 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6131
6135
6132
6136
6133 @command(
6137 @command(
6134 b'rollback',
6138 b'rollback',
6135 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6139 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6136 helpcategory=command.CATEGORY_MAINTENANCE,
6140 helpcategory=command.CATEGORY_MAINTENANCE,
6137 )
6141 )
6138 def rollback(ui, repo, **opts):
6142 def rollback(ui, repo, **opts):
6139 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6143 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6140
6144
6141 Please use :hg:`commit --amend` instead of rollback to correct
6145 Please use :hg:`commit --amend` instead of rollback to correct
6142 mistakes in the last commit.
6146 mistakes in the last commit.
6143
6147
6144 This command should be used with care. There is only one level of
6148 This command should be used with care. There is only one level of
6145 rollback, and there is no way to undo a rollback. It will also
6149 rollback, and there is no way to undo a rollback. It will also
6146 restore the dirstate at the time of the last transaction, losing
6150 restore the dirstate at the time of the last transaction, losing
6147 any dirstate changes since that time. This command does not alter
6151 any dirstate changes since that time. This command does not alter
6148 the working directory.
6152 the working directory.
6149
6153
6150 Transactions are used to encapsulate the effects of all commands
6154 Transactions are used to encapsulate the effects of all commands
6151 that create new changesets or propagate existing changesets into a
6155 that create new changesets or propagate existing changesets into a
6152 repository.
6156 repository.
6153
6157
6154 .. container:: verbose
6158 .. container:: verbose
6155
6159
6156 For example, the following commands are transactional, and their
6160 For example, the following commands are transactional, and their
6157 effects can be rolled back:
6161 effects can be rolled back:
6158
6162
6159 - commit
6163 - commit
6160 - import
6164 - import
6161 - pull
6165 - pull
6162 - push (with this repository as the destination)
6166 - push (with this repository as the destination)
6163 - unbundle
6167 - unbundle
6164
6168
6165 To avoid permanent data loss, rollback will refuse to rollback a
6169 To avoid permanent data loss, rollback will refuse to rollback a
6166 commit transaction if it isn't checked out. Use --force to
6170 commit transaction if it isn't checked out. Use --force to
6167 override this protection.
6171 override this protection.
6168
6172
6169 The rollback command can be entirely disabled by setting the
6173 The rollback command can be entirely disabled by setting the
6170 ``ui.rollback`` configuration setting to false. If you're here
6174 ``ui.rollback`` configuration setting to false. If you're here
6171 because you want to use rollback and it's disabled, you can
6175 because you want to use rollback and it's disabled, you can
6172 re-enable the command by setting ``ui.rollback`` to true.
6176 re-enable the command by setting ``ui.rollback`` to true.
6173
6177
6174 This command is not intended for use on public repositories. Once
6178 This command is not intended for use on public repositories. Once
6175 changes are visible for pull by other users, rolling a transaction
6179 changes are visible for pull by other users, rolling a transaction
6176 back locally is ineffective (someone else may already have pulled
6180 back locally is ineffective (someone else may already have pulled
6177 the changes). Furthermore, a race is possible with readers of the
6181 the changes). Furthermore, a race is possible with readers of the
6178 repository; for example an in-progress pull from the repository
6182 repository; for example an in-progress pull from the repository
6179 may fail if a rollback is performed.
6183 may fail if a rollback is performed.
6180
6184
6181 Returns 0 on success, 1 if no rollback data is available.
6185 Returns 0 on success, 1 if no rollback data is available.
6182 """
6186 """
6183 if not ui.configbool(b'ui', b'rollback'):
6187 if not ui.configbool(b'ui', b'rollback'):
6184 raise error.Abort(
6188 raise error.Abort(
6185 _(b'rollback is disabled because it is unsafe'),
6189 _(b'rollback is disabled because it is unsafe'),
6186 hint=b'see `hg help -v rollback` for information',
6190 hint=b'see `hg help -v rollback` for information',
6187 )
6191 )
6188 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6192 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6189
6193
6190
6194
6191 @command(
6195 @command(
6192 b'root',
6196 b'root',
6193 [] + formatteropts,
6197 [] + formatteropts,
6194 intents={INTENT_READONLY},
6198 intents={INTENT_READONLY},
6195 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6199 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6196 )
6200 )
6197 def root(ui, repo, **opts):
6201 def root(ui, repo, **opts):
6198 """print the root (top) of the current working directory
6202 """print the root (top) of the current working directory
6199
6203
6200 Print the root directory of the current repository.
6204 Print the root directory of the current repository.
6201
6205
6202 .. container:: verbose
6206 .. container:: verbose
6203
6207
6204 Template:
6208 Template:
6205
6209
6206 The following keywords are supported in addition to the common template
6210 The following keywords are supported in addition to the common template
6207 keywords and functions. See also :hg:`help templates`.
6211 keywords and functions. See also :hg:`help templates`.
6208
6212
6209 :hgpath: String. Path to the .hg directory.
6213 :hgpath: String. Path to the .hg directory.
6210 :storepath: String. Path to the directory holding versioned data.
6214 :storepath: String. Path to the directory holding versioned data.
6211
6215
6212 Returns 0 on success.
6216 Returns 0 on success.
6213 """
6217 """
6214 opts = pycompat.byteskwargs(opts)
6218 opts = pycompat.byteskwargs(opts)
6215 with ui.formatter(b'root', opts) as fm:
6219 with ui.formatter(b'root', opts) as fm:
6216 fm.startitem()
6220 fm.startitem()
6217 fm.write(b'reporoot', b'%s\n', repo.root)
6221 fm.write(b'reporoot', b'%s\n', repo.root)
6218 fm.data(hgpath=repo.path, storepath=repo.spath)
6222 fm.data(hgpath=repo.path, storepath=repo.spath)
6219
6223
6220
6224
6221 @command(
6225 @command(
6222 b'serve',
6226 b'serve',
6223 [
6227 [
6224 (
6228 (
6225 b'A',
6229 b'A',
6226 b'accesslog',
6230 b'accesslog',
6227 b'',
6231 b'',
6228 _(b'name of access log file to write to'),
6232 _(b'name of access log file to write to'),
6229 _(b'FILE'),
6233 _(b'FILE'),
6230 ),
6234 ),
6231 (b'd', b'daemon', None, _(b'run server in background')),
6235 (b'd', b'daemon', None, _(b'run server in background')),
6232 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6236 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6233 (
6237 (
6234 b'E',
6238 b'E',
6235 b'errorlog',
6239 b'errorlog',
6236 b'',
6240 b'',
6237 _(b'name of error log file to write to'),
6241 _(b'name of error log file to write to'),
6238 _(b'FILE'),
6242 _(b'FILE'),
6239 ),
6243 ),
6240 # use string type, then we can check if something was passed
6244 # use string type, then we can check if something was passed
6241 (
6245 (
6242 b'p',
6246 b'p',
6243 b'port',
6247 b'port',
6244 b'',
6248 b'',
6245 _(b'port to listen on (default: 8000)'),
6249 _(b'port to listen on (default: 8000)'),
6246 _(b'PORT'),
6250 _(b'PORT'),
6247 ),
6251 ),
6248 (
6252 (
6249 b'a',
6253 b'a',
6250 b'address',
6254 b'address',
6251 b'',
6255 b'',
6252 _(b'address to listen on (default: all interfaces)'),
6256 _(b'address to listen on (default: all interfaces)'),
6253 _(b'ADDR'),
6257 _(b'ADDR'),
6254 ),
6258 ),
6255 (
6259 (
6256 b'',
6260 b'',
6257 b'prefix',
6261 b'prefix',
6258 b'',
6262 b'',
6259 _(b'prefix path to serve from (default: server root)'),
6263 _(b'prefix path to serve from (default: server root)'),
6260 _(b'PREFIX'),
6264 _(b'PREFIX'),
6261 ),
6265 ),
6262 (
6266 (
6263 b'n',
6267 b'n',
6264 b'name',
6268 b'name',
6265 b'',
6269 b'',
6266 _(b'name to show in web pages (default: working directory)'),
6270 _(b'name to show in web pages (default: working directory)'),
6267 _(b'NAME'),
6271 _(b'NAME'),
6268 ),
6272 ),
6269 (
6273 (
6270 b'',
6274 b'',
6271 b'web-conf',
6275 b'web-conf',
6272 b'',
6276 b'',
6273 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6277 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6274 _(b'FILE'),
6278 _(b'FILE'),
6275 ),
6279 ),
6276 (
6280 (
6277 b'',
6281 b'',
6278 b'webdir-conf',
6282 b'webdir-conf',
6279 b'',
6283 b'',
6280 _(b'name of the hgweb config file (DEPRECATED)'),
6284 _(b'name of the hgweb config file (DEPRECATED)'),
6281 _(b'FILE'),
6285 _(b'FILE'),
6282 ),
6286 ),
6283 (
6287 (
6284 b'',
6288 b'',
6285 b'pid-file',
6289 b'pid-file',
6286 b'',
6290 b'',
6287 _(b'name of file to write process ID to'),
6291 _(b'name of file to write process ID to'),
6288 _(b'FILE'),
6292 _(b'FILE'),
6289 ),
6293 ),
6290 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6294 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6291 (
6295 (
6292 b'',
6296 b'',
6293 b'cmdserver',
6297 b'cmdserver',
6294 b'',
6298 b'',
6295 _(b'for remote clients (ADVANCED)'),
6299 _(b'for remote clients (ADVANCED)'),
6296 _(b'MODE'),
6300 _(b'MODE'),
6297 ),
6301 ),
6298 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6302 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6299 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6303 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6300 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6304 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6301 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6305 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6302 (b'', b'print-url', None, _(b'start and print only the URL')),
6306 (b'', b'print-url', None, _(b'start and print only the URL')),
6303 ]
6307 ]
6304 + subrepoopts,
6308 + subrepoopts,
6305 _(b'[OPTION]...'),
6309 _(b'[OPTION]...'),
6306 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6310 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6307 helpbasic=True,
6311 helpbasic=True,
6308 optionalrepo=True,
6312 optionalrepo=True,
6309 )
6313 )
6310 def serve(ui, repo, **opts):
6314 def serve(ui, repo, **opts):
6311 """start stand-alone webserver
6315 """start stand-alone webserver
6312
6316
6313 Start a local HTTP repository browser and pull server. You can use
6317 Start a local HTTP repository browser and pull server. You can use
6314 this for ad-hoc sharing and browsing of repositories. It is
6318 this for ad-hoc sharing and browsing of repositories. It is
6315 recommended to use a real web server to serve a repository for
6319 recommended to use a real web server to serve a repository for
6316 longer periods of time.
6320 longer periods of time.
6317
6321
6318 Please note that the server does not implement access control.
6322 Please note that the server does not implement access control.
6319 This means that, by default, anybody can read from the server and
6323 This means that, by default, anybody can read from the server and
6320 nobody can write to it by default. Set the ``web.allow-push``
6324 nobody can write to it by default. Set the ``web.allow-push``
6321 option to ``*`` to allow everybody to push to the server. You
6325 option to ``*`` to allow everybody to push to the server. You
6322 should use a real web server if you need to authenticate users.
6326 should use a real web server if you need to authenticate users.
6323
6327
6324 By default, the server logs accesses to stdout and errors to
6328 By default, the server logs accesses to stdout and errors to
6325 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6329 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6326 files.
6330 files.
6327
6331
6328 To have the server choose a free port number to listen on, specify
6332 To have the server choose a free port number to listen on, specify
6329 a port number of 0; in this case, the server will print the port
6333 a port number of 0; in this case, the server will print the port
6330 number it uses.
6334 number it uses.
6331
6335
6332 Returns 0 on success.
6336 Returns 0 on success.
6333 """
6337 """
6334
6338
6335 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6339 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6336 opts = pycompat.byteskwargs(opts)
6340 opts = pycompat.byteskwargs(opts)
6337 if opts[b"print_url"] and ui.verbose:
6341 if opts[b"print_url"] and ui.verbose:
6338 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6342 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6339
6343
6340 if opts[b"stdio"]:
6344 if opts[b"stdio"]:
6341 if repo is None:
6345 if repo is None:
6342 raise error.RepoError(
6346 raise error.RepoError(
6343 _(b"there is no Mercurial repository here (.hg not found)")
6347 _(b"there is no Mercurial repository here (.hg not found)")
6344 )
6348 )
6345 s = wireprotoserver.sshserver(ui, repo)
6349 s = wireprotoserver.sshserver(ui, repo)
6346 s.serve_forever()
6350 s.serve_forever()
6347
6351
6348 service = server.createservice(ui, repo, opts)
6352 service = server.createservice(ui, repo, opts)
6349 return server.runservice(opts, initfn=service.init, runfn=service.run)
6353 return server.runservice(opts, initfn=service.init, runfn=service.run)
6350
6354
6351
6355
6352 @command(
6356 @command(
6353 b'shelve',
6357 b'shelve',
6354 [
6358 [
6355 (
6359 (
6356 b'A',
6360 b'A',
6357 b'addremove',
6361 b'addremove',
6358 None,
6362 None,
6359 _(b'mark new/missing files as added/removed before shelving'),
6363 _(b'mark new/missing files as added/removed before shelving'),
6360 ),
6364 ),
6361 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6365 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6362 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6366 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6363 (
6367 (
6364 b'',
6368 b'',
6365 b'date',
6369 b'date',
6366 b'',
6370 b'',
6367 _(b'shelve with the specified commit date'),
6371 _(b'shelve with the specified commit date'),
6368 _(b'DATE'),
6372 _(b'DATE'),
6369 ),
6373 ),
6370 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6374 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6371 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6375 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6372 (
6376 (
6373 b'k',
6377 b'k',
6374 b'keep',
6378 b'keep',
6375 False,
6379 False,
6376 _(b'shelve, but keep changes in the working directory'),
6380 _(b'shelve, but keep changes in the working directory'),
6377 ),
6381 ),
6378 (b'l', b'list', None, _(b'list current shelves')),
6382 (b'l', b'list', None, _(b'list current shelves')),
6379 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6383 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6380 (
6384 (
6381 b'n',
6385 b'n',
6382 b'name',
6386 b'name',
6383 b'',
6387 b'',
6384 _(b'use the given name for the shelved commit'),
6388 _(b'use the given name for the shelved commit'),
6385 _(b'NAME'),
6389 _(b'NAME'),
6386 ),
6390 ),
6387 (
6391 (
6388 b'p',
6392 b'p',
6389 b'patch',
6393 b'patch',
6390 None,
6394 None,
6391 _(
6395 _(
6392 b'output patches for changes (provide the names of the shelved '
6396 b'output patches for changes (provide the names of the shelved '
6393 b'changes as positional arguments)'
6397 b'changes as positional arguments)'
6394 ),
6398 ),
6395 ),
6399 ),
6396 (b'i', b'interactive', None, _(b'interactive mode')),
6400 (b'i', b'interactive', None, _(b'interactive mode')),
6397 (
6401 (
6398 b'',
6402 b'',
6399 b'stat',
6403 b'stat',
6400 None,
6404 None,
6401 _(
6405 _(
6402 b'output diffstat-style summary of changes (provide the names of '
6406 b'output diffstat-style summary of changes (provide the names of '
6403 b'the shelved changes as positional arguments)'
6407 b'the shelved changes as positional arguments)'
6404 ),
6408 ),
6405 ),
6409 ),
6406 ]
6410 ]
6407 + cmdutil.walkopts,
6411 + cmdutil.walkopts,
6408 _(b'hg shelve [OPTION]... [FILE]...'),
6412 _(b'hg shelve [OPTION]... [FILE]...'),
6409 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6413 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6410 )
6414 )
6411 def shelve(ui, repo, *pats, **opts):
6415 def shelve(ui, repo, *pats, **opts):
6412 '''save and set aside changes from the working directory
6416 '''save and set aside changes from the working directory
6413
6417
6414 Shelving takes files that "hg status" reports as not clean, saves
6418 Shelving takes files that "hg status" reports as not clean, saves
6415 the modifications to a bundle (a shelved change), and reverts the
6419 the modifications to a bundle (a shelved change), and reverts the
6416 files so that their state in the working directory becomes clean.
6420 files so that their state in the working directory becomes clean.
6417
6421
6418 To restore these changes to the working directory, using "hg
6422 To restore these changes to the working directory, using "hg
6419 unshelve"; this will work even if you switch to a different
6423 unshelve"; this will work even if you switch to a different
6420 commit.
6424 commit.
6421
6425
6422 When no files are specified, "hg shelve" saves all not-clean
6426 When no files are specified, "hg shelve" saves all not-clean
6423 files. If specific files or directories are named, only changes to
6427 files. If specific files or directories are named, only changes to
6424 those files are shelved.
6428 those files are shelved.
6425
6429
6426 In bare shelve (when no files are specified, without interactive,
6430 In bare shelve (when no files are specified, without interactive,
6427 include and exclude option), shelving remembers information if the
6431 include and exclude option), shelving remembers information if the
6428 working directory was on newly created branch, in other words working
6432 working directory was on newly created branch, in other words working
6429 directory was on different branch than its first parent. In this
6433 directory was on different branch than its first parent. In this
6430 situation unshelving restores branch information to the working directory.
6434 situation unshelving restores branch information to the working directory.
6431
6435
6432 Each shelved change has a name that makes it easier to find later.
6436 Each shelved change has a name that makes it easier to find later.
6433 The name of a shelved change defaults to being based on the active
6437 The name of a shelved change defaults to being based on the active
6434 bookmark, or if there is no active bookmark, the current named
6438 bookmark, or if there is no active bookmark, the current named
6435 branch. To specify a different name, use ``--name``.
6439 branch. To specify a different name, use ``--name``.
6436
6440
6437 To see a list of existing shelved changes, use the ``--list``
6441 To see a list of existing shelved changes, use the ``--list``
6438 option. For each shelved change, this will print its name, age,
6442 option. For each shelved change, this will print its name, age,
6439 and description; use ``--patch`` or ``--stat`` for more details.
6443 and description; use ``--patch`` or ``--stat`` for more details.
6440
6444
6441 To delete specific shelved changes, use ``--delete``. To delete
6445 To delete specific shelved changes, use ``--delete``. To delete
6442 all shelved changes, use ``--cleanup``.
6446 all shelved changes, use ``--cleanup``.
6443 '''
6447 '''
6444 opts = pycompat.byteskwargs(opts)
6448 opts = pycompat.byteskwargs(opts)
6445 allowables = [
6449 allowables = [
6446 (b'addremove', {b'create'}), # 'create' is pseudo action
6450 (b'addremove', {b'create'}), # 'create' is pseudo action
6447 (b'unknown', {b'create'}),
6451 (b'unknown', {b'create'}),
6448 (b'cleanup', {b'cleanup'}),
6452 (b'cleanup', {b'cleanup'}),
6449 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6453 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6450 (b'delete', {b'delete'}),
6454 (b'delete', {b'delete'}),
6451 (b'edit', {b'create'}),
6455 (b'edit', {b'create'}),
6452 (b'keep', {b'create'}),
6456 (b'keep', {b'create'}),
6453 (b'list', {b'list'}),
6457 (b'list', {b'list'}),
6454 (b'message', {b'create'}),
6458 (b'message', {b'create'}),
6455 (b'name', {b'create'}),
6459 (b'name', {b'create'}),
6456 (b'patch', {b'patch', b'list'}),
6460 (b'patch', {b'patch', b'list'}),
6457 (b'stat', {b'stat', b'list'}),
6461 (b'stat', {b'stat', b'list'}),
6458 ]
6462 ]
6459
6463
6460 def checkopt(opt):
6464 def checkopt(opt):
6461 if opts.get(opt):
6465 if opts.get(opt):
6462 for i, allowable in allowables:
6466 for i, allowable in allowables:
6463 if opts[i] and opt not in allowable:
6467 if opts[i] and opt not in allowable:
6464 raise error.Abort(
6468 raise error.Abort(
6465 _(
6469 _(
6466 b"options '--%s' and '--%s' may not be "
6470 b"options '--%s' and '--%s' may not be "
6467 b"used together"
6471 b"used together"
6468 )
6472 )
6469 % (opt, i)
6473 % (opt, i)
6470 )
6474 )
6471 return True
6475 return True
6472
6476
6473 if checkopt(b'cleanup'):
6477 if checkopt(b'cleanup'):
6474 if pats:
6478 if pats:
6475 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6479 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6476 return shelvemod.cleanupcmd(ui, repo)
6480 return shelvemod.cleanupcmd(ui, repo)
6477 elif checkopt(b'delete'):
6481 elif checkopt(b'delete'):
6478 return shelvemod.deletecmd(ui, repo, pats)
6482 return shelvemod.deletecmd(ui, repo, pats)
6479 elif checkopt(b'list'):
6483 elif checkopt(b'list'):
6480 return shelvemod.listcmd(ui, repo, pats, opts)
6484 return shelvemod.listcmd(ui, repo, pats, opts)
6481 elif checkopt(b'patch') or checkopt(b'stat'):
6485 elif checkopt(b'patch') or checkopt(b'stat'):
6482 return shelvemod.patchcmds(ui, repo, pats, opts)
6486 return shelvemod.patchcmds(ui, repo, pats, opts)
6483 else:
6487 else:
6484 return shelvemod.createcmd(ui, repo, pats, opts)
6488 return shelvemod.createcmd(ui, repo, pats, opts)
6485
6489
6486
6490
6487 _NOTTERSE = b'nothing'
6491 _NOTTERSE = b'nothing'
6488
6492
6489
6493
6490 @command(
6494 @command(
6491 b'status|st',
6495 b'status|st',
6492 [
6496 [
6493 (b'A', b'all', None, _(b'show status of all files')),
6497 (b'A', b'all', None, _(b'show status of all files')),
6494 (b'm', b'modified', None, _(b'show only modified files')),
6498 (b'm', b'modified', None, _(b'show only modified files')),
6495 (b'a', b'added', None, _(b'show only added files')),
6499 (b'a', b'added', None, _(b'show only added files')),
6496 (b'r', b'removed', None, _(b'show only removed files')),
6500 (b'r', b'removed', None, _(b'show only removed files')),
6497 (b'd', b'deleted', None, _(b'show only missing files')),
6501 (b'd', b'deleted', None, _(b'show only missing files')),
6498 (b'c', b'clean', None, _(b'show only files without changes')),
6502 (b'c', b'clean', None, _(b'show only files without changes')),
6499 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6503 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6500 (b'i', b'ignored', None, _(b'show only ignored files')),
6504 (b'i', b'ignored', None, _(b'show only ignored files')),
6501 (b'n', b'no-status', None, _(b'hide status prefix')),
6505 (b'n', b'no-status', None, _(b'hide status prefix')),
6502 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6506 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6503 (
6507 (
6504 b'C',
6508 b'C',
6505 b'copies',
6509 b'copies',
6506 None,
6510 None,
6507 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6511 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6508 ),
6512 ),
6509 (
6513 (
6510 b'0',
6514 b'0',
6511 b'print0',
6515 b'print0',
6512 None,
6516 None,
6513 _(b'end filenames with NUL, for use with xargs'),
6517 _(b'end filenames with NUL, for use with xargs'),
6514 ),
6518 ),
6515 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6519 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6516 (
6520 (
6517 b'',
6521 b'',
6518 b'change',
6522 b'change',
6519 b'',
6523 b'',
6520 _(b'list the changed files of a revision'),
6524 _(b'list the changed files of a revision'),
6521 _(b'REV'),
6525 _(b'REV'),
6522 ),
6526 ),
6523 ]
6527 ]
6524 + walkopts
6528 + walkopts
6525 + subrepoopts
6529 + subrepoopts
6526 + formatteropts,
6530 + formatteropts,
6527 _(b'[OPTION]... [FILE]...'),
6531 _(b'[OPTION]... [FILE]...'),
6528 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6532 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6529 helpbasic=True,
6533 helpbasic=True,
6530 inferrepo=True,
6534 inferrepo=True,
6531 intents={INTENT_READONLY},
6535 intents={INTENT_READONLY},
6532 )
6536 )
6533 def status(ui, repo, *pats, **opts):
6537 def status(ui, repo, *pats, **opts):
6534 """show changed files in the working directory
6538 """show changed files in the working directory
6535
6539
6536 Show status of files in the repository. If names are given, only
6540 Show status of files in the repository. If names are given, only
6537 files that match are shown. Files that are clean or ignored or
6541 files that match are shown. Files that are clean or ignored or
6538 the source of a copy/move operation, are not listed unless
6542 the source of a copy/move operation, are not listed unless
6539 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6543 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6540 Unless options described with "show only ..." are given, the
6544 Unless options described with "show only ..." are given, the
6541 options -mardu are used.
6545 options -mardu are used.
6542
6546
6543 Option -q/--quiet hides untracked (unknown and ignored) files
6547 Option -q/--quiet hides untracked (unknown and ignored) files
6544 unless explicitly requested with -u/--unknown or -i/--ignored.
6548 unless explicitly requested with -u/--unknown or -i/--ignored.
6545
6549
6546 .. note::
6550 .. note::
6547
6551
6548 :hg:`status` may appear to disagree with diff if permissions have
6552 :hg:`status` may appear to disagree with diff if permissions have
6549 changed or a merge has occurred. The standard diff format does
6553 changed or a merge has occurred. The standard diff format does
6550 not report permission changes and diff only reports changes
6554 not report permission changes and diff only reports changes
6551 relative to one merge parent.
6555 relative to one merge parent.
6552
6556
6553 If one revision is given, it is used as the base revision.
6557 If one revision is given, it is used as the base revision.
6554 If two revisions are given, the differences between them are
6558 If two revisions are given, the differences between them are
6555 shown. The --change option can also be used as a shortcut to list
6559 shown. The --change option can also be used as a shortcut to list
6556 the changed files of a revision from its first parent.
6560 the changed files of a revision from its first parent.
6557
6561
6558 The codes used to show the status of files are::
6562 The codes used to show the status of files are::
6559
6563
6560 M = modified
6564 M = modified
6561 A = added
6565 A = added
6562 R = removed
6566 R = removed
6563 C = clean
6567 C = clean
6564 ! = missing (deleted by non-hg command, but still tracked)
6568 ! = missing (deleted by non-hg command, but still tracked)
6565 ? = not tracked
6569 ? = not tracked
6566 I = ignored
6570 I = ignored
6567 = origin of the previous file (with --copies)
6571 = origin of the previous file (with --copies)
6568
6572
6569 .. container:: verbose
6573 .. container:: verbose
6570
6574
6571 The -t/--terse option abbreviates the output by showing only the directory
6575 The -t/--terse option abbreviates the output by showing only the directory
6572 name if all the files in it share the same status. The option takes an
6576 name if all the files in it share the same status. The option takes an
6573 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6577 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6574 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6578 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6575 for 'ignored' and 'c' for clean.
6579 for 'ignored' and 'c' for clean.
6576
6580
6577 It abbreviates only those statuses which are passed. Note that clean and
6581 It abbreviates only those statuses which are passed. Note that clean and
6578 ignored files are not displayed with '--terse ic' unless the -c/--clean
6582 ignored files are not displayed with '--terse ic' unless the -c/--clean
6579 and -i/--ignored options are also used.
6583 and -i/--ignored options are also used.
6580
6584
6581 The -v/--verbose option shows information when the repository is in an
6585 The -v/--verbose option shows information when the repository is in an
6582 unfinished merge, shelve, rebase state etc. You can have this behavior
6586 unfinished merge, shelve, rebase state etc. You can have this behavior
6583 turned on by default by enabling the ``commands.status.verbose`` option.
6587 turned on by default by enabling the ``commands.status.verbose`` option.
6584
6588
6585 You can skip displaying some of these states by setting
6589 You can skip displaying some of these states by setting
6586 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6590 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6587 'histedit', 'merge', 'rebase', or 'unshelve'.
6591 'histedit', 'merge', 'rebase', or 'unshelve'.
6588
6592
6589 Template:
6593 Template:
6590
6594
6591 The following keywords are supported in addition to the common template
6595 The following keywords are supported in addition to the common template
6592 keywords and functions. See also :hg:`help templates`.
6596 keywords and functions. See also :hg:`help templates`.
6593
6597
6594 :path: String. Repository-absolute path of the file.
6598 :path: String. Repository-absolute path of the file.
6595 :source: String. Repository-absolute path of the file originated from.
6599 :source: String. Repository-absolute path of the file originated from.
6596 Available if ``--copies`` is specified.
6600 Available if ``--copies`` is specified.
6597 :status: String. Character denoting file's status.
6601 :status: String. Character denoting file's status.
6598
6602
6599 Examples:
6603 Examples:
6600
6604
6601 - show changes in the working directory relative to a
6605 - show changes in the working directory relative to a
6602 changeset::
6606 changeset::
6603
6607
6604 hg status --rev 9353
6608 hg status --rev 9353
6605
6609
6606 - show changes in the working directory relative to the
6610 - show changes in the working directory relative to the
6607 current directory (see :hg:`help patterns` for more information)::
6611 current directory (see :hg:`help patterns` for more information)::
6608
6612
6609 hg status re:
6613 hg status re:
6610
6614
6611 - show all changes including copies in an existing changeset::
6615 - show all changes including copies in an existing changeset::
6612
6616
6613 hg status --copies --change 9353
6617 hg status --copies --change 9353
6614
6618
6615 - get a NUL separated list of added files, suitable for xargs::
6619 - get a NUL separated list of added files, suitable for xargs::
6616
6620
6617 hg status -an0
6621 hg status -an0
6618
6622
6619 - show more information about the repository status, abbreviating
6623 - show more information about the repository status, abbreviating
6620 added, removed, modified, deleted, and untracked paths::
6624 added, removed, modified, deleted, and untracked paths::
6621
6625
6622 hg status -v -t mardu
6626 hg status -v -t mardu
6623
6627
6624 Returns 0 on success.
6628 Returns 0 on success.
6625
6629
6626 """
6630 """
6627
6631
6628 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6632 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6629 opts = pycompat.byteskwargs(opts)
6633 opts = pycompat.byteskwargs(opts)
6630 revs = opts.get(b'rev')
6634 revs = opts.get(b'rev')
6631 change = opts.get(b'change')
6635 change = opts.get(b'change')
6632 terse = opts.get(b'terse')
6636 terse = opts.get(b'terse')
6633 if terse is _NOTTERSE:
6637 if terse is _NOTTERSE:
6634 if revs:
6638 if revs:
6635 terse = b''
6639 terse = b''
6636 else:
6640 else:
6637 terse = ui.config(b'commands', b'status.terse')
6641 terse = ui.config(b'commands', b'status.terse')
6638
6642
6639 if revs and terse:
6643 if revs and terse:
6640 msg = _(b'cannot use --terse with --rev')
6644 msg = _(b'cannot use --terse with --rev')
6641 raise error.Abort(msg)
6645 raise error.Abort(msg)
6642 elif change:
6646 elif change:
6643 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6647 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6644 ctx2 = scmutil.revsingle(repo, change, None)
6648 ctx2 = scmutil.revsingle(repo, change, None)
6645 ctx1 = ctx2.p1()
6649 ctx1 = ctx2.p1()
6646 else:
6650 else:
6647 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6651 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6648 ctx1, ctx2 = scmutil.revpair(repo, revs)
6652 ctx1, ctx2 = scmutil.revpair(repo, revs)
6649
6653
6650 forcerelativevalue = None
6654 forcerelativevalue = None
6651 if ui.hasconfig(b'commands', b'status.relative'):
6655 if ui.hasconfig(b'commands', b'status.relative'):
6652 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6656 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6653 uipathfn = scmutil.getuipathfn(
6657 uipathfn = scmutil.getuipathfn(
6654 repo,
6658 repo,
6655 legacyrelativevalue=bool(pats),
6659 legacyrelativevalue=bool(pats),
6656 forcerelativevalue=forcerelativevalue,
6660 forcerelativevalue=forcerelativevalue,
6657 )
6661 )
6658
6662
6659 if opts.get(b'print0'):
6663 if opts.get(b'print0'):
6660 end = b'\0'
6664 end = b'\0'
6661 else:
6665 else:
6662 end = b'\n'
6666 end = b'\n'
6663 states = b'modified added removed deleted unknown ignored clean'.split()
6667 states = b'modified added removed deleted unknown ignored clean'.split()
6664 show = [k for k in states if opts.get(k)]
6668 show = [k for k in states if opts.get(k)]
6665 if opts.get(b'all'):
6669 if opts.get(b'all'):
6666 show += ui.quiet and (states[:4] + [b'clean']) or states
6670 show += ui.quiet and (states[:4] + [b'clean']) or states
6667
6671
6668 if not show:
6672 if not show:
6669 if ui.quiet:
6673 if ui.quiet:
6670 show = states[:4]
6674 show = states[:4]
6671 else:
6675 else:
6672 show = states[:5]
6676 show = states[:5]
6673
6677
6674 m = scmutil.match(ctx2, pats, opts)
6678 m = scmutil.match(ctx2, pats, opts)
6675 if terse:
6679 if terse:
6676 # we need to compute clean and unknown to terse
6680 # we need to compute clean and unknown to terse
6677 stat = repo.status(
6681 stat = repo.status(
6678 ctx1.node(),
6682 ctx1.node(),
6679 ctx2.node(),
6683 ctx2.node(),
6680 m,
6684 m,
6681 b'ignored' in show or b'i' in terse,
6685 b'ignored' in show or b'i' in terse,
6682 clean=True,
6686 clean=True,
6683 unknown=True,
6687 unknown=True,
6684 listsubrepos=opts.get(b'subrepos'),
6688 listsubrepos=opts.get(b'subrepos'),
6685 )
6689 )
6686
6690
6687 stat = cmdutil.tersedir(stat, terse)
6691 stat = cmdutil.tersedir(stat, terse)
6688 else:
6692 else:
6689 stat = repo.status(
6693 stat = repo.status(
6690 ctx1.node(),
6694 ctx1.node(),
6691 ctx2.node(),
6695 ctx2.node(),
6692 m,
6696 m,
6693 b'ignored' in show,
6697 b'ignored' in show,
6694 b'clean' in show,
6698 b'clean' in show,
6695 b'unknown' in show,
6699 b'unknown' in show,
6696 opts.get(b'subrepos'),
6700 opts.get(b'subrepos'),
6697 )
6701 )
6698
6702
6699 changestates = zip(
6703 changestates = zip(
6700 states,
6704 states,
6701 pycompat.iterbytestr(b'MAR!?IC'),
6705 pycompat.iterbytestr(b'MAR!?IC'),
6702 [getattr(stat, s.decode('utf8')) for s in states],
6706 [getattr(stat, s.decode('utf8')) for s in states],
6703 )
6707 )
6704
6708
6705 copy = {}
6709 copy = {}
6706 if (
6710 if (
6707 opts.get(b'all')
6711 opts.get(b'all')
6708 or opts.get(b'copies')
6712 or opts.get(b'copies')
6709 or ui.configbool(b'ui', b'statuscopies')
6713 or ui.configbool(b'ui', b'statuscopies')
6710 ) and not opts.get(b'no_status'):
6714 ) and not opts.get(b'no_status'):
6711 copy = copies.pathcopies(ctx1, ctx2, m)
6715 copy = copies.pathcopies(ctx1, ctx2, m)
6712
6716
6713 morestatus = None
6717 morestatus = None
6714 if (
6718 if (
6715 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6719 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6716 ) and not ui.plain():
6720 ) and not ui.plain():
6717 morestatus = cmdutil.readmorestatus(repo)
6721 morestatus = cmdutil.readmorestatus(repo)
6718
6722
6719 ui.pager(b'status')
6723 ui.pager(b'status')
6720 fm = ui.formatter(b'status', opts)
6724 fm = ui.formatter(b'status', opts)
6721 fmt = b'%s' + end
6725 fmt = b'%s' + end
6722 showchar = not opts.get(b'no_status')
6726 showchar = not opts.get(b'no_status')
6723
6727
6724 for state, char, files in changestates:
6728 for state, char, files in changestates:
6725 if state in show:
6729 if state in show:
6726 label = b'status.' + state
6730 label = b'status.' + state
6727 for f in files:
6731 for f in files:
6728 fm.startitem()
6732 fm.startitem()
6729 fm.context(ctx=ctx2)
6733 fm.context(ctx=ctx2)
6730 fm.data(itemtype=b'file', path=f)
6734 fm.data(itemtype=b'file', path=f)
6731 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6735 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6732 fm.plain(fmt % uipathfn(f), label=label)
6736 fm.plain(fmt % uipathfn(f), label=label)
6733 if f in copy:
6737 if f in copy:
6734 fm.data(source=copy[f])
6738 fm.data(source=copy[f])
6735 fm.plain(
6739 fm.plain(
6736 (b' %s' + end) % uipathfn(copy[f]),
6740 (b' %s' + end) % uipathfn(copy[f]),
6737 label=b'status.copied',
6741 label=b'status.copied',
6738 )
6742 )
6739 if morestatus:
6743 if morestatus:
6740 morestatus.formatfile(f, fm)
6744 morestatus.formatfile(f, fm)
6741
6745
6742 if morestatus:
6746 if morestatus:
6743 morestatus.formatfooter(fm)
6747 morestatus.formatfooter(fm)
6744 fm.end()
6748 fm.end()
6745
6749
6746
6750
6747 @command(
6751 @command(
6748 b'summary|sum',
6752 b'summary|sum',
6749 [(b'', b'remote', None, _(b'check for push and pull'))],
6753 [(b'', b'remote', None, _(b'check for push and pull'))],
6750 b'[--remote]',
6754 b'[--remote]',
6751 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6755 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6752 helpbasic=True,
6756 helpbasic=True,
6753 intents={INTENT_READONLY},
6757 intents={INTENT_READONLY},
6754 )
6758 )
6755 def summary(ui, repo, **opts):
6759 def summary(ui, repo, **opts):
6756 """summarize working directory state
6760 """summarize working directory state
6757
6761
6758 This generates a brief summary of the working directory state,
6762 This generates a brief summary of the working directory state,
6759 including parents, branch, commit status, phase and available updates.
6763 including parents, branch, commit status, phase and available updates.
6760
6764
6761 With the --remote option, this will check the default paths for
6765 With the --remote option, this will check the default paths for
6762 incoming and outgoing changes. This can be time-consuming.
6766 incoming and outgoing changes. This can be time-consuming.
6763
6767
6764 Returns 0 on success.
6768 Returns 0 on success.
6765 """
6769 """
6766
6770
6767 opts = pycompat.byteskwargs(opts)
6771 opts = pycompat.byteskwargs(opts)
6768 ui.pager(b'summary')
6772 ui.pager(b'summary')
6769 ctx = repo[None]
6773 ctx = repo[None]
6770 parents = ctx.parents()
6774 parents = ctx.parents()
6771 pnode = parents[0].node()
6775 pnode = parents[0].node()
6772 marks = []
6776 marks = []
6773
6777
6774 try:
6778 try:
6775 ms = mergestatemod.mergestate.read(repo)
6779 ms = mergestatemod.mergestate.read(repo)
6776 except error.UnsupportedMergeRecords as e:
6780 except error.UnsupportedMergeRecords as e:
6777 s = b' '.join(e.recordtypes)
6781 s = b' '.join(e.recordtypes)
6778 ui.warn(
6782 ui.warn(
6779 _(b'warning: merge state has unsupported record types: %s\n') % s
6783 _(b'warning: merge state has unsupported record types: %s\n') % s
6780 )
6784 )
6781 unresolved = []
6785 unresolved = []
6782 else:
6786 else:
6783 unresolved = list(ms.unresolved())
6787 unresolved = list(ms.unresolved())
6784
6788
6785 for p in parents:
6789 for p in parents:
6786 # label with log.changeset (instead of log.parent) since this
6790 # label with log.changeset (instead of log.parent) since this
6787 # shows a working directory parent *changeset*:
6791 # shows a working directory parent *changeset*:
6788 # i18n: column positioning for "hg summary"
6792 # i18n: column positioning for "hg summary"
6789 ui.write(
6793 ui.write(
6790 _(b'parent: %d:%s ') % (p.rev(), p),
6794 _(b'parent: %d:%s ') % (p.rev(), p),
6791 label=logcmdutil.changesetlabels(p),
6795 label=logcmdutil.changesetlabels(p),
6792 )
6796 )
6793 ui.write(b' '.join(p.tags()), label=b'log.tag')
6797 ui.write(b' '.join(p.tags()), label=b'log.tag')
6794 if p.bookmarks():
6798 if p.bookmarks():
6795 marks.extend(p.bookmarks())
6799 marks.extend(p.bookmarks())
6796 if p.rev() == -1:
6800 if p.rev() == -1:
6797 if not len(repo):
6801 if not len(repo):
6798 ui.write(_(b' (empty repository)'))
6802 ui.write(_(b' (empty repository)'))
6799 else:
6803 else:
6800 ui.write(_(b' (no revision checked out)'))
6804 ui.write(_(b' (no revision checked out)'))
6801 if p.obsolete():
6805 if p.obsolete():
6802 ui.write(_(b' (obsolete)'))
6806 ui.write(_(b' (obsolete)'))
6803 if p.isunstable():
6807 if p.isunstable():
6804 instabilities = (
6808 instabilities = (
6805 ui.label(instability, b'trouble.%s' % instability)
6809 ui.label(instability, b'trouble.%s' % instability)
6806 for instability in p.instabilities()
6810 for instability in p.instabilities()
6807 )
6811 )
6808 ui.write(b' (' + b', '.join(instabilities) + b')')
6812 ui.write(b' (' + b', '.join(instabilities) + b')')
6809 ui.write(b'\n')
6813 ui.write(b'\n')
6810 if p.description():
6814 if p.description():
6811 ui.status(
6815 ui.status(
6812 b' ' + p.description().splitlines()[0].strip() + b'\n',
6816 b' ' + p.description().splitlines()[0].strip() + b'\n',
6813 label=b'log.summary',
6817 label=b'log.summary',
6814 )
6818 )
6815
6819
6816 branch = ctx.branch()
6820 branch = ctx.branch()
6817 bheads = repo.branchheads(branch)
6821 bheads = repo.branchheads(branch)
6818 # i18n: column positioning for "hg summary"
6822 # i18n: column positioning for "hg summary"
6819 m = _(b'branch: %s\n') % branch
6823 m = _(b'branch: %s\n') % branch
6820 if branch != b'default':
6824 if branch != b'default':
6821 ui.write(m, label=b'log.branch')
6825 ui.write(m, label=b'log.branch')
6822 else:
6826 else:
6823 ui.status(m, label=b'log.branch')
6827 ui.status(m, label=b'log.branch')
6824
6828
6825 if marks:
6829 if marks:
6826 active = repo._activebookmark
6830 active = repo._activebookmark
6827 # i18n: column positioning for "hg summary"
6831 # i18n: column positioning for "hg summary"
6828 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6832 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6829 if active is not None:
6833 if active is not None:
6830 if active in marks:
6834 if active in marks:
6831 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6835 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6832 marks.remove(active)
6836 marks.remove(active)
6833 else:
6837 else:
6834 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6838 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6835 for m in marks:
6839 for m in marks:
6836 ui.write(b' ' + m, label=b'log.bookmark')
6840 ui.write(b' ' + m, label=b'log.bookmark')
6837 ui.write(b'\n', label=b'log.bookmark')
6841 ui.write(b'\n', label=b'log.bookmark')
6838
6842
6839 status = repo.status(unknown=True)
6843 status = repo.status(unknown=True)
6840
6844
6841 c = repo.dirstate.copies()
6845 c = repo.dirstate.copies()
6842 copied, renamed = [], []
6846 copied, renamed = [], []
6843 for d, s in pycompat.iteritems(c):
6847 for d, s in pycompat.iteritems(c):
6844 if s in status.removed:
6848 if s in status.removed:
6845 status.removed.remove(s)
6849 status.removed.remove(s)
6846 renamed.append(d)
6850 renamed.append(d)
6847 else:
6851 else:
6848 copied.append(d)
6852 copied.append(d)
6849 if d in status.added:
6853 if d in status.added:
6850 status.added.remove(d)
6854 status.added.remove(d)
6851
6855
6852 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6856 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6853
6857
6854 labels = [
6858 labels = [
6855 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6859 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6856 (ui.label(_(b'%d added'), b'status.added'), status.added),
6860 (ui.label(_(b'%d added'), b'status.added'), status.added),
6857 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6861 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6858 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6862 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6859 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6863 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6860 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6864 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6861 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6865 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6862 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6866 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6863 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6867 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6864 ]
6868 ]
6865 t = []
6869 t = []
6866 for l, s in labels:
6870 for l, s in labels:
6867 if s:
6871 if s:
6868 t.append(l % len(s))
6872 t.append(l % len(s))
6869
6873
6870 t = b', '.join(t)
6874 t = b', '.join(t)
6871 cleanworkdir = False
6875 cleanworkdir = False
6872
6876
6873 if repo.vfs.exists(b'graftstate'):
6877 if repo.vfs.exists(b'graftstate'):
6874 t += _(b' (graft in progress)')
6878 t += _(b' (graft in progress)')
6875 if repo.vfs.exists(b'updatestate'):
6879 if repo.vfs.exists(b'updatestate'):
6876 t += _(b' (interrupted update)')
6880 t += _(b' (interrupted update)')
6877 elif len(parents) > 1:
6881 elif len(parents) > 1:
6878 t += _(b' (merge)')
6882 t += _(b' (merge)')
6879 elif branch != parents[0].branch():
6883 elif branch != parents[0].branch():
6880 t += _(b' (new branch)')
6884 t += _(b' (new branch)')
6881 elif parents[0].closesbranch() and pnode in repo.branchheads(
6885 elif parents[0].closesbranch() and pnode in repo.branchheads(
6882 branch, closed=True
6886 branch, closed=True
6883 ):
6887 ):
6884 t += _(b' (head closed)')
6888 t += _(b' (head closed)')
6885 elif not (
6889 elif not (
6886 status.modified
6890 status.modified
6887 or status.added
6891 or status.added
6888 or status.removed
6892 or status.removed
6889 or renamed
6893 or renamed
6890 or copied
6894 or copied
6891 or subs
6895 or subs
6892 ):
6896 ):
6893 t += _(b' (clean)')
6897 t += _(b' (clean)')
6894 cleanworkdir = True
6898 cleanworkdir = True
6895 elif pnode not in bheads:
6899 elif pnode not in bheads:
6896 t += _(b' (new branch head)')
6900 t += _(b' (new branch head)')
6897
6901
6898 if parents:
6902 if parents:
6899 pendingphase = max(p.phase() for p in parents)
6903 pendingphase = max(p.phase() for p in parents)
6900 else:
6904 else:
6901 pendingphase = phases.public
6905 pendingphase = phases.public
6902
6906
6903 if pendingphase > phases.newcommitphase(ui):
6907 if pendingphase > phases.newcommitphase(ui):
6904 t += b' (%s)' % phases.phasenames[pendingphase]
6908 t += b' (%s)' % phases.phasenames[pendingphase]
6905
6909
6906 if cleanworkdir:
6910 if cleanworkdir:
6907 # i18n: column positioning for "hg summary"
6911 # i18n: column positioning for "hg summary"
6908 ui.status(_(b'commit: %s\n') % t.strip())
6912 ui.status(_(b'commit: %s\n') % t.strip())
6909 else:
6913 else:
6910 # i18n: column positioning for "hg summary"
6914 # i18n: column positioning for "hg summary"
6911 ui.write(_(b'commit: %s\n') % t.strip())
6915 ui.write(_(b'commit: %s\n') % t.strip())
6912
6916
6913 # all ancestors of branch heads - all ancestors of parent = new csets
6917 # all ancestors of branch heads - all ancestors of parent = new csets
6914 new = len(
6918 new = len(
6915 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
6919 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
6916 )
6920 )
6917
6921
6918 if new == 0:
6922 if new == 0:
6919 # i18n: column positioning for "hg summary"
6923 # i18n: column positioning for "hg summary"
6920 ui.status(_(b'update: (current)\n'))
6924 ui.status(_(b'update: (current)\n'))
6921 elif pnode not in bheads:
6925 elif pnode not in bheads:
6922 # i18n: column positioning for "hg summary"
6926 # i18n: column positioning for "hg summary"
6923 ui.write(_(b'update: %d new changesets (update)\n') % new)
6927 ui.write(_(b'update: %d new changesets (update)\n') % new)
6924 else:
6928 else:
6925 # i18n: column positioning for "hg summary"
6929 # i18n: column positioning for "hg summary"
6926 ui.write(
6930 ui.write(
6927 _(b'update: %d new changesets, %d branch heads (merge)\n')
6931 _(b'update: %d new changesets, %d branch heads (merge)\n')
6928 % (new, len(bheads))
6932 % (new, len(bheads))
6929 )
6933 )
6930
6934
6931 t = []
6935 t = []
6932 draft = len(repo.revs(b'draft()'))
6936 draft = len(repo.revs(b'draft()'))
6933 if draft:
6937 if draft:
6934 t.append(_(b'%d draft') % draft)
6938 t.append(_(b'%d draft') % draft)
6935 secret = len(repo.revs(b'secret()'))
6939 secret = len(repo.revs(b'secret()'))
6936 if secret:
6940 if secret:
6937 t.append(_(b'%d secret') % secret)
6941 t.append(_(b'%d secret') % secret)
6938
6942
6939 if draft or secret:
6943 if draft or secret:
6940 ui.status(_(b'phases: %s\n') % b', '.join(t))
6944 ui.status(_(b'phases: %s\n') % b', '.join(t))
6941
6945
6942 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6946 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6943 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
6947 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
6944 numtrouble = len(repo.revs(trouble + b"()"))
6948 numtrouble = len(repo.revs(trouble + b"()"))
6945 # We write all the possibilities to ease translation
6949 # We write all the possibilities to ease translation
6946 troublemsg = {
6950 troublemsg = {
6947 b"orphan": _(b"orphan: %d changesets"),
6951 b"orphan": _(b"orphan: %d changesets"),
6948 b"contentdivergent": _(b"content-divergent: %d changesets"),
6952 b"contentdivergent": _(b"content-divergent: %d changesets"),
6949 b"phasedivergent": _(b"phase-divergent: %d changesets"),
6953 b"phasedivergent": _(b"phase-divergent: %d changesets"),
6950 }
6954 }
6951 if numtrouble > 0:
6955 if numtrouble > 0:
6952 ui.status(troublemsg[trouble] % numtrouble + b"\n")
6956 ui.status(troublemsg[trouble] % numtrouble + b"\n")
6953
6957
6954 cmdutil.summaryhooks(ui, repo)
6958 cmdutil.summaryhooks(ui, repo)
6955
6959
6956 if opts.get(b'remote'):
6960 if opts.get(b'remote'):
6957 needsincoming, needsoutgoing = True, True
6961 needsincoming, needsoutgoing = True, True
6958 else:
6962 else:
6959 needsincoming, needsoutgoing = False, False
6963 needsincoming, needsoutgoing = False, False
6960 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6964 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6961 if i:
6965 if i:
6962 needsincoming = True
6966 needsincoming = True
6963 if o:
6967 if o:
6964 needsoutgoing = True
6968 needsoutgoing = True
6965 if not needsincoming and not needsoutgoing:
6969 if not needsincoming and not needsoutgoing:
6966 return
6970 return
6967
6971
6968 def getincoming():
6972 def getincoming():
6969 source, branches = hg.parseurl(ui.expandpath(b'default'))
6973 source, branches = hg.parseurl(ui.expandpath(b'default'))
6970 sbranch = branches[0]
6974 sbranch = branches[0]
6971 try:
6975 try:
6972 other = hg.peer(repo, {}, source)
6976 other = hg.peer(repo, {}, source)
6973 except error.RepoError:
6977 except error.RepoError:
6974 if opts.get(b'remote'):
6978 if opts.get(b'remote'):
6975 raise
6979 raise
6976 return source, sbranch, None, None, None
6980 return source, sbranch, None, None, None
6977 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6981 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6978 if revs:
6982 if revs:
6979 revs = [other.lookup(rev) for rev in revs]
6983 revs = [other.lookup(rev) for rev in revs]
6980 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
6984 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
6981 repo.ui.pushbuffer()
6985 repo.ui.pushbuffer()
6982 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6986 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6983 repo.ui.popbuffer()
6987 repo.ui.popbuffer()
6984 return source, sbranch, other, commoninc, commoninc[1]
6988 return source, sbranch, other, commoninc, commoninc[1]
6985
6989
6986 if needsincoming:
6990 if needsincoming:
6987 source, sbranch, sother, commoninc, incoming = getincoming()
6991 source, sbranch, sother, commoninc, incoming = getincoming()
6988 else:
6992 else:
6989 source = sbranch = sother = commoninc = incoming = None
6993 source = sbranch = sother = commoninc = incoming = None
6990
6994
6991 def getoutgoing():
6995 def getoutgoing():
6992 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
6996 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
6993 dbranch = branches[0]
6997 dbranch = branches[0]
6994 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6998 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6995 if source != dest:
6999 if source != dest:
6996 try:
7000 try:
6997 dother = hg.peer(repo, {}, dest)
7001 dother = hg.peer(repo, {}, dest)
6998 except error.RepoError:
7002 except error.RepoError:
6999 if opts.get(b'remote'):
7003 if opts.get(b'remote'):
7000 raise
7004 raise
7001 return dest, dbranch, None, None
7005 return dest, dbranch, None, None
7002 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7006 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7003 elif sother is None:
7007 elif sother is None:
7004 # there is no explicit destination peer, but source one is invalid
7008 # there is no explicit destination peer, but source one is invalid
7005 return dest, dbranch, None, None
7009 return dest, dbranch, None, None
7006 else:
7010 else:
7007 dother = sother
7011 dother = sother
7008 if source != dest or (sbranch is not None and sbranch != dbranch):
7012 if source != dest or (sbranch is not None and sbranch != dbranch):
7009 common = None
7013 common = None
7010 else:
7014 else:
7011 common = commoninc
7015 common = commoninc
7012 if revs:
7016 if revs:
7013 revs = [repo.lookup(rev) for rev in revs]
7017 revs = [repo.lookup(rev) for rev in revs]
7014 repo.ui.pushbuffer()
7018 repo.ui.pushbuffer()
7015 outgoing = discovery.findcommonoutgoing(
7019 outgoing = discovery.findcommonoutgoing(
7016 repo, dother, onlyheads=revs, commoninc=common
7020 repo, dother, onlyheads=revs, commoninc=common
7017 )
7021 )
7018 repo.ui.popbuffer()
7022 repo.ui.popbuffer()
7019 return dest, dbranch, dother, outgoing
7023 return dest, dbranch, dother, outgoing
7020
7024
7021 if needsoutgoing:
7025 if needsoutgoing:
7022 dest, dbranch, dother, outgoing = getoutgoing()
7026 dest, dbranch, dother, outgoing = getoutgoing()
7023 else:
7027 else:
7024 dest = dbranch = dother = outgoing = None
7028 dest = dbranch = dother = outgoing = None
7025
7029
7026 if opts.get(b'remote'):
7030 if opts.get(b'remote'):
7027 t = []
7031 t = []
7028 if incoming:
7032 if incoming:
7029 t.append(_(b'1 or more incoming'))
7033 t.append(_(b'1 or more incoming'))
7030 o = outgoing.missing
7034 o = outgoing.missing
7031 if o:
7035 if o:
7032 t.append(_(b'%d outgoing') % len(o))
7036 t.append(_(b'%d outgoing') % len(o))
7033 other = dother or sother
7037 other = dother or sother
7034 if b'bookmarks' in other.listkeys(b'namespaces'):
7038 if b'bookmarks' in other.listkeys(b'namespaces'):
7035 counts = bookmarks.summary(repo, other)
7039 counts = bookmarks.summary(repo, other)
7036 if counts[0] > 0:
7040 if counts[0] > 0:
7037 t.append(_(b'%d incoming bookmarks') % counts[0])
7041 t.append(_(b'%d incoming bookmarks') % counts[0])
7038 if counts[1] > 0:
7042 if counts[1] > 0:
7039 t.append(_(b'%d outgoing bookmarks') % counts[1])
7043 t.append(_(b'%d outgoing bookmarks') % counts[1])
7040
7044
7041 if t:
7045 if t:
7042 # i18n: column positioning for "hg summary"
7046 # i18n: column positioning for "hg summary"
7043 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7047 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7044 else:
7048 else:
7045 # i18n: column positioning for "hg summary"
7049 # i18n: column positioning for "hg summary"
7046 ui.status(_(b'remote: (synced)\n'))
7050 ui.status(_(b'remote: (synced)\n'))
7047
7051
7048 cmdutil.summaryremotehooks(
7052 cmdutil.summaryremotehooks(
7049 ui,
7053 ui,
7050 repo,
7054 repo,
7051 opts,
7055 opts,
7052 (
7056 (
7053 (source, sbranch, sother, commoninc),
7057 (source, sbranch, sother, commoninc),
7054 (dest, dbranch, dother, outgoing),
7058 (dest, dbranch, dother, outgoing),
7055 ),
7059 ),
7056 )
7060 )
7057
7061
7058
7062
7059 @command(
7063 @command(
7060 b'tag',
7064 b'tag',
7061 [
7065 [
7062 (b'f', b'force', None, _(b'force tag')),
7066 (b'f', b'force', None, _(b'force tag')),
7063 (b'l', b'local', None, _(b'make the tag local')),
7067 (b'l', b'local', None, _(b'make the tag local')),
7064 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7068 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7065 (b'', b'remove', None, _(b'remove a tag')),
7069 (b'', b'remove', None, _(b'remove a tag')),
7066 # -l/--local is already there, commitopts cannot be used
7070 # -l/--local is already there, commitopts cannot be used
7067 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7071 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7068 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7072 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7069 ]
7073 ]
7070 + commitopts2,
7074 + commitopts2,
7071 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7075 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7072 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7076 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7073 )
7077 )
7074 def tag(ui, repo, name1, *names, **opts):
7078 def tag(ui, repo, name1, *names, **opts):
7075 """add one or more tags for the current or given revision
7079 """add one or more tags for the current or given revision
7076
7080
7077 Name a particular revision using <name>.
7081 Name a particular revision using <name>.
7078
7082
7079 Tags are used to name particular revisions of the repository and are
7083 Tags are used to name particular revisions of the repository and are
7080 very useful to compare different revisions, to go back to significant
7084 very useful to compare different revisions, to go back to significant
7081 earlier versions or to mark branch points as releases, etc. Changing
7085 earlier versions or to mark branch points as releases, etc. Changing
7082 an existing tag is normally disallowed; use -f/--force to override.
7086 an existing tag is normally disallowed; use -f/--force to override.
7083
7087
7084 If no revision is given, the parent of the working directory is
7088 If no revision is given, the parent of the working directory is
7085 used.
7089 used.
7086
7090
7087 To facilitate version control, distribution, and merging of tags,
7091 To facilitate version control, distribution, and merging of tags,
7088 they are stored as a file named ".hgtags" which is managed similarly
7092 they are stored as a file named ".hgtags" which is managed similarly
7089 to other project files and can be hand-edited if necessary. This
7093 to other project files and can be hand-edited if necessary. This
7090 also means that tagging creates a new commit. The file
7094 also means that tagging creates a new commit. The file
7091 ".hg/localtags" is used for local tags (not shared among
7095 ".hg/localtags" is used for local tags (not shared among
7092 repositories).
7096 repositories).
7093
7097
7094 Tag commits are usually made at the head of a branch. If the parent
7098 Tag commits are usually made at the head of a branch. If the parent
7095 of the working directory is not a branch head, :hg:`tag` aborts; use
7099 of the working directory is not a branch head, :hg:`tag` aborts; use
7096 -f/--force to force the tag commit to be based on a non-head
7100 -f/--force to force the tag commit to be based on a non-head
7097 changeset.
7101 changeset.
7098
7102
7099 See :hg:`help dates` for a list of formats valid for -d/--date.
7103 See :hg:`help dates` for a list of formats valid for -d/--date.
7100
7104
7101 Since tag names have priority over branch names during revision
7105 Since tag names have priority over branch names during revision
7102 lookup, using an existing branch name as a tag name is discouraged.
7106 lookup, using an existing branch name as a tag name is discouraged.
7103
7107
7104 Returns 0 on success.
7108 Returns 0 on success.
7105 """
7109 """
7106 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7110 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7107 opts = pycompat.byteskwargs(opts)
7111 opts = pycompat.byteskwargs(opts)
7108 with repo.wlock(), repo.lock():
7112 with repo.wlock(), repo.lock():
7109 rev_ = b"."
7113 rev_ = b"."
7110 names = [t.strip() for t in (name1,) + names]
7114 names = [t.strip() for t in (name1,) + names]
7111 if len(names) != len(set(names)):
7115 if len(names) != len(set(names)):
7112 raise error.Abort(_(b'tag names must be unique'))
7116 raise error.Abort(_(b'tag names must be unique'))
7113 for n in names:
7117 for n in names:
7114 scmutil.checknewlabel(repo, n, b'tag')
7118 scmutil.checknewlabel(repo, n, b'tag')
7115 if not n:
7119 if not n:
7116 raise error.Abort(
7120 raise error.Abort(
7117 _(b'tag names cannot consist entirely of whitespace')
7121 _(b'tag names cannot consist entirely of whitespace')
7118 )
7122 )
7119 if opts.get(b'rev'):
7123 if opts.get(b'rev'):
7120 rev_ = opts[b'rev']
7124 rev_ = opts[b'rev']
7121 message = opts.get(b'message')
7125 message = opts.get(b'message')
7122 if opts.get(b'remove'):
7126 if opts.get(b'remove'):
7123 if opts.get(b'local'):
7127 if opts.get(b'local'):
7124 expectedtype = b'local'
7128 expectedtype = b'local'
7125 else:
7129 else:
7126 expectedtype = b'global'
7130 expectedtype = b'global'
7127
7131
7128 for n in names:
7132 for n in names:
7129 if repo.tagtype(n) == b'global':
7133 if repo.tagtype(n) == b'global':
7130 alltags = tagsmod.findglobaltags(ui, repo)
7134 alltags = tagsmod.findglobaltags(ui, repo)
7131 if alltags[n][0] == nullid:
7135 if alltags[n][0] == nullid:
7132 raise error.Abort(_(b"tag '%s' is already removed") % n)
7136 raise error.Abort(_(b"tag '%s' is already removed") % n)
7133 if not repo.tagtype(n):
7137 if not repo.tagtype(n):
7134 raise error.Abort(_(b"tag '%s' does not exist") % n)
7138 raise error.Abort(_(b"tag '%s' does not exist") % n)
7135 if repo.tagtype(n) != expectedtype:
7139 if repo.tagtype(n) != expectedtype:
7136 if expectedtype == b'global':
7140 if expectedtype == b'global':
7137 raise error.Abort(
7141 raise error.Abort(
7138 _(b"tag '%s' is not a global tag") % n
7142 _(b"tag '%s' is not a global tag") % n
7139 )
7143 )
7140 else:
7144 else:
7141 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7145 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7142 rev_ = b'null'
7146 rev_ = b'null'
7143 if not message:
7147 if not message:
7144 # we don't translate commit messages
7148 # we don't translate commit messages
7145 message = b'Removed tag %s' % b', '.join(names)
7149 message = b'Removed tag %s' % b', '.join(names)
7146 elif not opts.get(b'force'):
7150 elif not opts.get(b'force'):
7147 for n in names:
7151 for n in names:
7148 if n in repo.tags():
7152 if n in repo.tags():
7149 raise error.Abort(
7153 raise error.Abort(
7150 _(b"tag '%s' already exists (use -f to force)") % n
7154 _(b"tag '%s' already exists (use -f to force)") % n
7151 )
7155 )
7152 if not opts.get(b'local'):
7156 if not opts.get(b'local'):
7153 p1, p2 = repo.dirstate.parents()
7157 p1, p2 = repo.dirstate.parents()
7154 if p2 != nullid:
7158 if p2 != nullid:
7155 raise error.Abort(_(b'uncommitted merge'))
7159 raise error.Abort(_(b'uncommitted merge'))
7156 bheads = repo.branchheads()
7160 bheads = repo.branchheads()
7157 if not opts.get(b'force') and bheads and p1 not in bheads:
7161 if not opts.get(b'force') and bheads and p1 not in bheads:
7158 raise error.Abort(
7162 raise error.Abort(
7159 _(
7163 _(
7160 b'working directory is not at a branch head '
7164 b'working directory is not at a branch head '
7161 b'(use -f to force)'
7165 b'(use -f to force)'
7162 )
7166 )
7163 )
7167 )
7164 node = scmutil.revsingle(repo, rev_).node()
7168 node = scmutil.revsingle(repo, rev_).node()
7165
7169
7166 if not message:
7170 if not message:
7167 # we don't translate commit messages
7171 # we don't translate commit messages
7168 message = b'Added tag %s for changeset %s' % (
7172 message = b'Added tag %s for changeset %s' % (
7169 b', '.join(names),
7173 b', '.join(names),
7170 short(node),
7174 short(node),
7171 )
7175 )
7172
7176
7173 date = opts.get(b'date')
7177 date = opts.get(b'date')
7174 if date:
7178 if date:
7175 date = dateutil.parsedate(date)
7179 date = dateutil.parsedate(date)
7176
7180
7177 if opts.get(b'remove'):
7181 if opts.get(b'remove'):
7178 editform = b'tag.remove'
7182 editform = b'tag.remove'
7179 else:
7183 else:
7180 editform = b'tag.add'
7184 editform = b'tag.add'
7181 editor = cmdutil.getcommiteditor(
7185 editor = cmdutil.getcommiteditor(
7182 editform=editform, **pycompat.strkwargs(opts)
7186 editform=editform, **pycompat.strkwargs(opts)
7183 )
7187 )
7184
7188
7185 # don't allow tagging the null rev
7189 # don't allow tagging the null rev
7186 if (
7190 if (
7187 not opts.get(b'remove')
7191 not opts.get(b'remove')
7188 and scmutil.revsingle(repo, rev_).rev() == nullrev
7192 and scmutil.revsingle(repo, rev_).rev() == nullrev
7189 ):
7193 ):
7190 raise error.Abort(_(b"cannot tag null revision"))
7194 raise error.Abort(_(b"cannot tag null revision"))
7191
7195
7192 tagsmod.tag(
7196 tagsmod.tag(
7193 repo,
7197 repo,
7194 names,
7198 names,
7195 node,
7199 node,
7196 message,
7200 message,
7197 opts.get(b'local'),
7201 opts.get(b'local'),
7198 opts.get(b'user'),
7202 opts.get(b'user'),
7199 date,
7203 date,
7200 editor=editor,
7204 editor=editor,
7201 )
7205 )
7202
7206
7203
7207
7204 @command(
7208 @command(
7205 b'tags',
7209 b'tags',
7206 formatteropts,
7210 formatteropts,
7207 b'',
7211 b'',
7208 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7212 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7209 intents={INTENT_READONLY},
7213 intents={INTENT_READONLY},
7210 )
7214 )
7211 def tags(ui, repo, **opts):
7215 def tags(ui, repo, **opts):
7212 """list repository tags
7216 """list repository tags
7213
7217
7214 This lists both regular and local tags. When the -v/--verbose
7218 This lists both regular and local tags. When the -v/--verbose
7215 switch is used, a third column "local" is printed for local tags.
7219 switch is used, a third column "local" is printed for local tags.
7216 When the -q/--quiet switch is used, only the tag name is printed.
7220 When the -q/--quiet switch is used, only the tag name is printed.
7217
7221
7218 .. container:: verbose
7222 .. container:: verbose
7219
7223
7220 Template:
7224 Template:
7221
7225
7222 The following keywords are supported in addition to the common template
7226 The following keywords are supported in addition to the common template
7223 keywords and functions such as ``{tag}``. See also
7227 keywords and functions such as ``{tag}``. See also
7224 :hg:`help templates`.
7228 :hg:`help templates`.
7225
7229
7226 :type: String. ``local`` for local tags.
7230 :type: String. ``local`` for local tags.
7227
7231
7228 Returns 0 on success.
7232 Returns 0 on success.
7229 """
7233 """
7230
7234
7231 opts = pycompat.byteskwargs(opts)
7235 opts = pycompat.byteskwargs(opts)
7232 ui.pager(b'tags')
7236 ui.pager(b'tags')
7233 fm = ui.formatter(b'tags', opts)
7237 fm = ui.formatter(b'tags', opts)
7234 hexfunc = fm.hexfunc
7238 hexfunc = fm.hexfunc
7235
7239
7236 for t, n in reversed(repo.tagslist()):
7240 for t, n in reversed(repo.tagslist()):
7237 hn = hexfunc(n)
7241 hn = hexfunc(n)
7238 label = b'tags.normal'
7242 label = b'tags.normal'
7239 tagtype = b''
7243 tagtype = b''
7240 if repo.tagtype(t) == b'local':
7244 if repo.tagtype(t) == b'local':
7241 label = b'tags.local'
7245 label = b'tags.local'
7242 tagtype = b'local'
7246 tagtype = b'local'
7243
7247
7244 fm.startitem()
7248 fm.startitem()
7245 fm.context(repo=repo)
7249 fm.context(repo=repo)
7246 fm.write(b'tag', b'%s', t, label=label)
7250 fm.write(b'tag', b'%s', t, label=label)
7247 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7251 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7248 fm.condwrite(
7252 fm.condwrite(
7249 not ui.quiet,
7253 not ui.quiet,
7250 b'rev node',
7254 b'rev node',
7251 fmt,
7255 fmt,
7252 repo.changelog.rev(n),
7256 repo.changelog.rev(n),
7253 hn,
7257 hn,
7254 label=label,
7258 label=label,
7255 )
7259 )
7256 fm.condwrite(
7260 fm.condwrite(
7257 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7261 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7258 )
7262 )
7259 fm.plain(b'\n')
7263 fm.plain(b'\n')
7260 fm.end()
7264 fm.end()
7261
7265
7262
7266
7263 @command(
7267 @command(
7264 b'tip',
7268 b'tip',
7265 [
7269 [
7266 (b'p', b'patch', None, _(b'show patch')),
7270 (b'p', b'patch', None, _(b'show patch')),
7267 (b'g', b'git', None, _(b'use git extended diff format')),
7271 (b'g', b'git', None, _(b'use git extended diff format')),
7268 ]
7272 ]
7269 + templateopts,
7273 + templateopts,
7270 _(b'[-p] [-g]'),
7274 _(b'[-p] [-g]'),
7271 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7275 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7272 )
7276 )
7273 def tip(ui, repo, **opts):
7277 def tip(ui, repo, **opts):
7274 """show the tip revision (DEPRECATED)
7278 """show the tip revision (DEPRECATED)
7275
7279
7276 The tip revision (usually just called the tip) is the changeset
7280 The tip revision (usually just called the tip) is the changeset
7277 most recently added to the repository (and therefore the most
7281 most recently added to the repository (and therefore the most
7278 recently changed head).
7282 recently changed head).
7279
7283
7280 If you have just made a commit, that commit will be the tip. If
7284 If you have just made a commit, that commit will be the tip. If
7281 you have just pulled changes from another repository, the tip of
7285 you have just pulled changes from another repository, the tip of
7282 that repository becomes the current tip. The "tip" tag is special
7286 that repository becomes the current tip. The "tip" tag is special
7283 and cannot be renamed or assigned to a different changeset.
7287 and cannot be renamed or assigned to a different changeset.
7284
7288
7285 This command is deprecated, please use :hg:`heads` instead.
7289 This command is deprecated, please use :hg:`heads` instead.
7286
7290
7287 Returns 0 on success.
7291 Returns 0 on success.
7288 """
7292 """
7289 opts = pycompat.byteskwargs(opts)
7293 opts = pycompat.byteskwargs(opts)
7290 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7294 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7291 displayer.show(repo[b'tip'])
7295 displayer.show(repo[b'tip'])
7292 displayer.close()
7296 displayer.close()
7293
7297
7294
7298
7295 @command(
7299 @command(
7296 b'unbundle',
7300 b'unbundle',
7297 [
7301 [
7298 (
7302 (
7299 b'u',
7303 b'u',
7300 b'update',
7304 b'update',
7301 None,
7305 None,
7302 _(b'update to new branch head if changesets were unbundled'),
7306 _(b'update to new branch head if changesets were unbundled'),
7303 )
7307 )
7304 ],
7308 ],
7305 _(b'[-u] FILE...'),
7309 _(b'[-u] FILE...'),
7306 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7310 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7307 )
7311 )
7308 def unbundle(ui, repo, fname1, *fnames, **opts):
7312 def unbundle(ui, repo, fname1, *fnames, **opts):
7309 """apply one or more bundle files
7313 """apply one or more bundle files
7310
7314
7311 Apply one or more bundle files generated by :hg:`bundle`.
7315 Apply one or more bundle files generated by :hg:`bundle`.
7312
7316
7313 Returns 0 on success, 1 if an update has unresolved files.
7317 Returns 0 on success, 1 if an update has unresolved files.
7314 """
7318 """
7315 fnames = (fname1,) + fnames
7319 fnames = (fname1,) + fnames
7316
7320
7317 with repo.lock():
7321 with repo.lock():
7318 for fname in fnames:
7322 for fname in fnames:
7319 f = hg.openpath(ui, fname)
7323 f = hg.openpath(ui, fname)
7320 gen = exchange.readbundle(ui, f, fname)
7324 gen = exchange.readbundle(ui, f, fname)
7321 if isinstance(gen, streamclone.streamcloneapplier):
7325 if isinstance(gen, streamclone.streamcloneapplier):
7322 raise error.Abort(
7326 raise error.Abort(
7323 _(
7327 _(
7324 b'packed bundles cannot be applied with '
7328 b'packed bundles cannot be applied with '
7325 b'"hg unbundle"'
7329 b'"hg unbundle"'
7326 ),
7330 ),
7327 hint=_(b'use "hg debugapplystreamclonebundle"'),
7331 hint=_(b'use "hg debugapplystreamclonebundle"'),
7328 )
7332 )
7329 url = b'bundle:' + fname
7333 url = b'bundle:' + fname
7330 try:
7334 try:
7331 txnname = b'unbundle'
7335 txnname = b'unbundle'
7332 if not isinstance(gen, bundle2.unbundle20):
7336 if not isinstance(gen, bundle2.unbundle20):
7333 txnname = b'unbundle\n%s' % util.hidepassword(url)
7337 txnname = b'unbundle\n%s' % util.hidepassword(url)
7334 with repo.transaction(txnname) as tr:
7338 with repo.transaction(txnname) as tr:
7335 op = bundle2.applybundle(
7339 op = bundle2.applybundle(
7336 repo, gen, tr, source=b'unbundle', url=url
7340 repo, gen, tr, source=b'unbundle', url=url
7337 )
7341 )
7338 except error.BundleUnknownFeatureError as exc:
7342 except error.BundleUnknownFeatureError as exc:
7339 raise error.Abort(
7343 raise error.Abort(
7340 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7344 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7341 hint=_(
7345 hint=_(
7342 b"see https://mercurial-scm.org/"
7346 b"see https://mercurial-scm.org/"
7343 b"wiki/BundleFeature for more "
7347 b"wiki/BundleFeature for more "
7344 b"information"
7348 b"information"
7345 ),
7349 ),
7346 )
7350 )
7347 modheads = bundle2.combinechangegroupresults(op)
7351 modheads = bundle2.combinechangegroupresults(op)
7348
7352
7349 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7353 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7350
7354
7351
7355
7352 @command(
7356 @command(
7353 b'unshelve',
7357 b'unshelve',
7354 [
7358 [
7355 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7359 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7356 (
7360 (
7357 b'c',
7361 b'c',
7358 b'continue',
7362 b'continue',
7359 None,
7363 None,
7360 _(b'continue an incomplete unshelve operation'),
7364 _(b'continue an incomplete unshelve operation'),
7361 ),
7365 ),
7362 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7366 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7363 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7367 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7364 (
7368 (
7365 b'n',
7369 b'n',
7366 b'name',
7370 b'name',
7367 b'',
7371 b'',
7368 _(b'restore shelved change with given name'),
7372 _(b'restore shelved change with given name'),
7369 _(b'NAME'),
7373 _(b'NAME'),
7370 ),
7374 ),
7371 (b't', b'tool', b'', _(b'specify merge tool')),
7375 (b't', b'tool', b'', _(b'specify merge tool')),
7372 (
7376 (
7373 b'',
7377 b'',
7374 b'date',
7378 b'date',
7375 b'',
7379 b'',
7376 _(b'set date for temporary commits (DEPRECATED)'),
7380 _(b'set date for temporary commits (DEPRECATED)'),
7377 _(b'DATE'),
7381 _(b'DATE'),
7378 ),
7382 ),
7379 ],
7383 ],
7380 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7384 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7381 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7385 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7382 )
7386 )
7383 def unshelve(ui, repo, *shelved, **opts):
7387 def unshelve(ui, repo, *shelved, **opts):
7384 """restore a shelved change to the working directory
7388 """restore a shelved change to the working directory
7385
7389
7386 This command accepts an optional name of a shelved change to
7390 This command accepts an optional name of a shelved change to
7387 restore. If none is given, the most recent shelved change is used.
7391 restore. If none is given, the most recent shelved change is used.
7388
7392
7389 If a shelved change is applied successfully, the bundle that
7393 If a shelved change is applied successfully, the bundle that
7390 contains the shelved changes is moved to a backup location
7394 contains the shelved changes is moved to a backup location
7391 (.hg/shelve-backup).
7395 (.hg/shelve-backup).
7392
7396
7393 Since you can restore a shelved change on top of an arbitrary
7397 Since you can restore a shelved change on top of an arbitrary
7394 commit, it is possible that unshelving will result in a conflict
7398 commit, it is possible that unshelving will result in a conflict
7395 between your changes and the commits you are unshelving onto. If
7399 between your changes and the commits you are unshelving onto. If
7396 this occurs, you must resolve the conflict, then use
7400 this occurs, you must resolve the conflict, then use
7397 ``--continue`` to complete the unshelve operation. (The bundle
7401 ``--continue`` to complete the unshelve operation. (The bundle
7398 will not be moved until you successfully complete the unshelve.)
7402 will not be moved until you successfully complete the unshelve.)
7399
7403
7400 (Alternatively, you can use ``--abort`` to abandon an unshelve
7404 (Alternatively, you can use ``--abort`` to abandon an unshelve
7401 that causes a conflict. This reverts the unshelved changes, and
7405 that causes a conflict. This reverts the unshelved changes, and
7402 leaves the bundle in place.)
7406 leaves the bundle in place.)
7403
7407
7404 If bare shelved change (without interactive, include and exclude
7408 If bare shelved change (without interactive, include and exclude
7405 option) was done on newly created branch it would restore branch
7409 option) was done on newly created branch it would restore branch
7406 information to the working directory.
7410 information to the working directory.
7407
7411
7408 After a successful unshelve, the shelved changes are stored in a
7412 After a successful unshelve, the shelved changes are stored in a
7409 backup directory. Only the N most recent backups are kept. N
7413 backup directory. Only the N most recent backups are kept. N
7410 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7414 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7411 configuration option.
7415 configuration option.
7412
7416
7413 .. container:: verbose
7417 .. container:: verbose
7414
7418
7415 Timestamp in seconds is used to decide order of backups. More
7419 Timestamp in seconds is used to decide order of backups. More
7416 than ``maxbackups`` backups are kept, if same timestamp
7420 than ``maxbackups`` backups are kept, if same timestamp
7417 prevents from deciding exact order of them, for safety.
7421 prevents from deciding exact order of them, for safety.
7418
7422
7419 Selected changes can be unshelved with ``--interactive`` flag.
7423 Selected changes can be unshelved with ``--interactive`` flag.
7420 The working directory is updated with the selected changes, and
7424 The working directory is updated with the selected changes, and
7421 only the unselected changes remain shelved.
7425 only the unselected changes remain shelved.
7422 Note: The whole shelve is applied to working directory first before
7426 Note: The whole shelve is applied to working directory first before
7423 running interactively. So, this will bring up all the conflicts between
7427 running interactively. So, this will bring up all the conflicts between
7424 working directory and the shelve, irrespective of which changes will be
7428 working directory and the shelve, irrespective of which changes will be
7425 unshelved.
7429 unshelved.
7426 """
7430 """
7427 with repo.wlock():
7431 with repo.wlock():
7428 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7432 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7429
7433
7430
7434
7431 statemod.addunfinished(
7435 statemod.addunfinished(
7432 b'unshelve',
7436 b'unshelve',
7433 fname=b'shelvedstate',
7437 fname=b'shelvedstate',
7434 continueflag=True,
7438 continueflag=True,
7435 abortfunc=shelvemod.hgabortunshelve,
7439 abortfunc=shelvemod.hgabortunshelve,
7436 continuefunc=shelvemod.hgcontinueunshelve,
7440 continuefunc=shelvemod.hgcontinueunshelve,
7437 cmdmsg=_(b'unshelve already in progress'),
7441 cmdmsg=_(b'unshelve already in progress'),
7438 )
7442 )
7439
7443
7440
7444
7441 @command(
7445 @command(
7442 b'update|up|checkout|co',
7446 b'update|up|checkout|co',
7443 [
7447 [
7444 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7448 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7445 (b'c', b'check', None, _(b'require clean working directory')),
7449 (b'c', b'check', None, _(b'require clean working directory')),
7446 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7450 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7447 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7451 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7448 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7452 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7449 ]
7453 ]
7450 + mergetoolopts,
7454 + mergetoolopts,
7451 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7455 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7452 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7456 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7453 helpbasic=True,
7457 helpbasic=True,
7454 )
7458 )
7455 def update(ui, repo, node=None, **opts):
7459 def update(ui, repo, node=None, **opts):
7456 """update working directory (or switch revisions)
7460 """update working directory (or switch revisions)
7457
7461
7458 Update the repository's working directory to the specified
7462 Update the repository's working directory to the specified
7459 changeset. If no changeset is specified, update to the tip of the
7463 changeset. If no changeset is specified, update to the tip of the
7460 current named branch and move the active bookmark (see :hg:`help
7464 current named branch and move the active bookmark (see :hg:`help
7461 bookmarks`).
7465 bookmarks`).
7462
7466
7463 Update sets the working directory's parent revision to the specified
7467 Update sets the working directory's parent revision to the specified
7464 changeset (see :hg:`help parents`).
7468 changeset (see :hg:`help parents`).
7465
7469
7466 If the changeset is not a descendant or ancestor of the working
7470 If the changeset is not a descendant or ancestor of the working
7467 directory's parent and there are uncommitted changes, the update is
7471 directory's parent and there are uncommitted changes, the update is
7468 aborted. With the -c/--check option, the working directory is checked
7472 aborted. With the -c/--check option, the working directory is checked
7469 for uncommitted changes; if none are found, the working directory is
7473 for uncommitted changes; if none are found, the working directory is
7470 updated to the specified changeset.
7474 updated to the specified changeset.
7471
7475
7472 .. container:: verbose
7476 .. container:: verbose
7473
7477
7474 The -C/--clean, -c/--check, and -m/--merge options control what
7478 The -C/--clean, -c/--check, and -m/--merge options control what
7475 happens if the working directory contains uncommitted changes.
7479 happens if the working directory contains uncommitted changes.
7476 At most of one of them can be specified.
7480 At most of one of them can be specified.
7477
7481
7478 1. If no option is specified, and if
7482 1. If no option is specified, and if
7479 the requested changeset is an ancestor or descendant of
7483 the requested changeset is an ancestor or descendant of
7480 the working directory's parent, the uncommitted changes
7484 the working directory's parent, the uncommitted changes
7481 are merged into the requested changeset and the merged
7485 are merged into the requested changeset and the merged
7482 result is left uncommitted. If the requested changeset is
7486 result is left uncommitted. If the requested changeset is
7483 not an ancestor or descendant (that is, it is on another
7487 not an ancestor or descendant (that is, it is on another
7484 branch), the update is aborted and the uncommitted changes
7488 branch), the update is aborted and the uncommitted changes
7485 are preserved.
7489 are preserved.
7486
7490
7487 2. With the -m/--merge option, the update is allowed even if the
7491 2. With the -m/--merge option, the update is allowed even if the
7488 requested changeset is not an ancestor or descendant of
7492 requested changeset is not an ancestor or descendant of
7489 the working directory's parent.
7493 the working directory's parent.
7490
7494
7491 3. With the -c/--check option, the update is aborted and the
7495 3. With the -c/--check option, the update is aborted and the
7492 uncommitted changes are preserved.
7496 uncommitted changes are preserved.
7493
7497
7494 4. With the -C/--clean option, uncommitted changes are discarded and
7498 4. With the -C/--clean option, uncommitted changes are discarded and
7495 the working directory is updated to the requested changeset.
7499 the working directory is updated to the requested changeset.
7496
7500
7497 To cancel an uncommitted merge (and lose your changes), use
7501 To cancel an uncommitted merge (and lose your changes), use
7498 :hg:`merge --abort`.
7502 :hg:`merge --abort`.
7499
7503
7500 Use null as the changeset to remove the working directory (like
7504 Use null as the changeset to remove the working directory (like
7501 :hg:`clone -U`).
7505 :hg:`clone -U`).
7502
7506
7503 If you want to revert just one file to an older revision, use
7507 If you want to revert just one file to an older revision, use
7504 :hg:`revert [-r REV] NAME`.
7508 :hg:`revert [-r REV] NAME`.
7505
7509
7506 See :hg:`help dates` for a list of formats valid for -d/--date.
7510 See :hg:`help dates` for a list of formats valid for -d/--date.
7507
7511
7508 Returns 0 on success, 1 if there are unresolved files.
7512 Returns 0 on success, 1 if there are unresolved files.
7509 """
7513 """
7510 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7514 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7511 rev = opts.get('rev')
7515 rev = opts.get('rev')
7512 date = opts.get('date')
7516 date = opts.get('date')
7513 clean = opts.get('clean')
7517 clean = opts.get('clean')
7514 check = opts.get('check')
7518 check = opts.get('check')
7515 merge = opts.get('merge')
7519 merge = opts.get('merge')
7516 if rev and node:
7520 if rev and node:
7517 raise error.Abort(_(b"please specify just one revision"))
7521 raise error.Abort(_(b"please specify just one revision"))
7518
7522
7519 if ui.configbool(b'commands', b'update.requiredest'):
7523 if ui.configbool(b'commands', b'update.requiredest'):
7520 if not node and not rev and not date:
7524 if not node and not rev and not date:
7521 raise error.Abort(
7525 raise error.Abort(
7522 _(b'you must specify a destination'),
7526 _(b'you must specify a destination'),
7523 hint=_(b'for example: hg update ".::"'),
7527 hint=_(b'for example: hg update ".::"'),
7524 )
7528 )
7525
7529
7526 if rev is None or rev == b'':
7530 if rev is None or rev == b'':
7527 rev = node
7531 rev = node
7528
7532
7529 if date and rev is not None:
7533 if date and rev is not None:
7530 raise error.Abort(_(b"you can't specify a revision and a date"))
7534 raise error.Abort(_(b"you can't specify a revision and a date"))
7531
7535
7532 updatecheck = None
7536 updatecheck = None
7533 if check:
7537 if check:
7534 updatecheck = b'abort'
7538 updatecheck = b'abort'
7535 elif merge:
7539 elif merge:
7536 updatecheck = b'none'
7540 updatecheck = b'none'
7537
7541
7538 with repo.wlock():
7542 with repo.wlock():
7539 cmdutil.clearunfinished(repo)
7543 cmdutil.clearunfinished(repo)
7540 if date:
7544 if date:
7541 rev = cmdutil.finddate(ui, repo, date)
7545 rev = cmdutil.finddate(ui, repo, date)
7542
7546
7543 # if we defined a bookmark, we have to remember the original name
7547 # if we defined a bookmark, we have to remember the original name
7544 brev = rev
7548 brev = rev
7545 if rev:
7549 if rev:
7546 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7550 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7547 ctx = scmutil.revsingle(repo, rev, default=None)
7551 ctx = scmutil.revsingle(repo, rev, default=None)
7548 rev = ctx.rev()
7552 rev = ctx.rev()
7549 hidden = ctx.hidden()
7553 hidden = ctx.hidden()
7550 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7554 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7551 with ui.configoverride(overrides, b'update'):
7555 with ui.configoverride(overrides, b'update'):
7552 ret = hg.updatetotally(
7556 ret = hg.updatetotally(
7553 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7557 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7554 )
7558 )
7555 if hidden:
7559 if hidden:
7556 ctxstr = ctx.hex()[:12]
7560 ctxstr = ctx.hex()[:12]
7557 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7561 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7558
7562
7559 if ctx.obsolete():
7563 if ctx.obsolete():
7560 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7564 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7561 ui.warn(b"(%s)\n" % obsfatemsg)
7565 ui.warn(b"(%s)\n" % obsfatemsg)
7562 return ret
7566 return ret
7563
7567
7564
7568
7565 @command(
7569 @command(
7566 b'verify',
7570 b'verify',
7567 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7571 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7568 helpcategory=command.CATEGORY_MAINTENANCE,
7572 helpcategory=command.CATEGORY_MAINTENANCE,
7569 )
7573 )
7570 def verify(ui, repo, **opts):
7574 def verify(ui, repo, **opts):
7571 """verify the integrity of the repository
7575 """verify the integrity of the repository
7572
7576
7573 Verify the integrity of the current repository.
7577 Verify the integrity of the current repository.
7574
7578
7575 This will perform an extensive check of the repository's
7579 This will perform an extensive check of the repository's
7576 integrity, validating the hashes and checksums of each entry in
7580 integrity, validating the hashes and checksums of each entry in
7577 the changelog, manifest, and tracked files, as well as the
7581 the changelog, manifest, and tracked files, as well as the
7578 integrity of their crosslinks and indices.
7582 integrity of their crosslinks and indices.
7579
7583
7580 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7584 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7581 for more information about recovery from corruption of the
7585 for more information about recovery from corruption of the
7582 repository.
7586 repository.
7583
7587
7584 Returns 0 on success, 1 if errors are encountered.
7588 Returns 0 on success, 1 if errors are encountered.
7585 """
7589 """
7586 opts = pycompat.byteskwargs(opts)
7590 opts = pycompat.byteskwargs(opts)
7587
7591
7588 level = None
7592 level = None
7589 if opts[b'full']:
7593 if opts[b'full']:
7590 level = verifymod.VERIFY_FULL
7594 level = verifymod.VERIFY_FULL
7591 return hg.verify(repo, level)
7595 return hg.verify(repo, level)
7592
7596
7593
7597
7594 @command(
7598 @command(
7595 b'version',
7599 b'version',
7596 [] + formatteropts,
7600 [] + formatteropts,
7597 helpcategory=command.CATEGORY_HELP,
7601 helpcategory=command.CATEGORY_HELP,
7598 norepo=True,
7602 norepo=True,
7599 intents={INTENT_READONLY},
7603 intents={INTENT_READONLY},
7600 )
7604 )
7601 def version_(ui, **opts):
7605 def version_(ui, **opts):
7602 """output version and copyright information
7606 """output version and copyright information
7603
7607
7604 .. container:: verbose
7608 .. container:: verbose
7605
7609
7606 Template:
7610 Template:
7607
7611
7608 The following keywords are supported. See also :hg:`help templates`.
7612 The following keywords are supported. See also :hg:`help templates`.
7609
7613
7610 :extensions: List of extensions.
7614 :extensions: List of extensions.
7611 :ver: String. Version number.
7615 :ver: String. Version number.
7612
7616
7613 And each entry of ``{extensions}`` provides the following sub-keywords
7617 And each entry of ``{extensions}`` provides the following sub-keywords
7614 in addition to ``{ver}``.
7618 in addition to ``{ver}``.
7615
7619
7616 :bundled: Boolean. True if included in the release.
7620 :bundled: Boolean. True if included in the release.
7617 :name: String. Extension name.
7621 :name: String. Extension name.
7618 """
7622 """
7619 opts = pycompat.byteskwargs(opts)
7623 opts = pycompat.byteskwargs(opts)
7620 if ui.verbose:
7624 if ui.verbose:
7621 ui.pager(b'version')
7625 ui.pager(b'version')
7622 fm = ui.formatter(b"version", opts)
7626 fm = ui.formatter(b"version", opts)
7623 fm.startitem()
7627 fm.startitem()
7624 fm.write(
7628 fm.write(
7625 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7629 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7626 )
7630 )
7627 license = _(
7631 license = _(
7628 b"(see https://mercurial-scm.org for more information)\n"
7632 b"(see https://mercurial-scm.org for more information)\n"
7629 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7633 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7630 b"This is free software; see the source for copying conditions. "
7634 b"This is free software; see the source for copying conditions. "
7631 b"There is NO\nwarranty; "
7635 b"There is NO\nwarranty; "
7632 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7636 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7633 )
7637 )
7634 if not ui.quiet:
7638 if not ui.quiet:
7635 fm.plain(license)
7639 fm.plain(license)
7636
7640
7637 if ui.verbose:
7641 if ui.verbose:
7638 fm.plain(_(b"\nEnabled extensions:\n\n"))
7642 fm.plain(_(b"\nEnabled extensions:\n\n"))
7639 # format names and versions into columns
7643 # format names and versions into columns
7640 names = []
7644 names = []
7641 vers = []
7645 vers = []
7642 isinternals = []
7646 isinternals = []
7643 for name, module in sorted(extensions.extensions()):
7647 for name, module in sorted(extensions.extensions()):
7644 names.append(name)
7648 names.append(name)
7645 vers.append(extensions.moduleversion(module) or None)
7649 vers.append(extensions.moduleversion(module) or None)
7646 isinternals.append(extensions.ismoduleinternal(module))
7650 isinternals.append(extensions.ismoduleinternal(module))
7647 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7651 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7648 if names:
7652 if names:
7649 namefmt = b" %%-%ds " % max(len(n) for n in names)
7653 namefmt = b" %%-%ds " % max(len(n) for n in names)
7650 places = [_(b"external"), _(b"internal")]
7654 places = [_(b"external"), _(b"internal")]
7651 for n, v, p in zip(names, vers, isinternals):
7655 for n, v, p in zip(names, vers, isinternals):
7652 fn.startitem()
7656 fn.startitem()
7653 fn.condwrite(ui.verbose, b"name", namefmt, n)
7657 fn.condwrite(ui.verbose, b"name", namefmt, n)
7654 if ui.verbose:
7658 if ui.verbose:
7655 fn.plain(b"%s " % places[p])
7659 fn.plain(b"%s " % places[p])
7656 fn.data(bundled=p)
7660 fn.data(bundled=p)
7657 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7661 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7658 if ui.verbose:
7662 if ui.verbose:
7659 fn.plain(b"\n")
7663 fn.plain(b"\n")
7660 fn.end()
7664 fn.end()
7661 fm.end()
7665 fm.end()
7662
7666
7663
7667
7664 def loadcmdtable(ui, name, cmdtable):
7668 def loadcmdtable(ui, name, cmdtable):
7665 """Load command functions from specified cmdtable
7669 """Load command functions from specified cmdtable
7666 """
7670 """
7667 overrides = [cmd for cmd in cmdtable if cmd in table]
7671 overrides = [cmd for cmd in cmdtable if cmd in table]
7668 if overrides:
7672 if overrides:
7669 ui.warn(
7673 ui.warn(
7670 _(b"extension '%s' overrides commands: %s\n")
7674 _(b"extension '%s' overrides commands: %s\n")
7671 % (name, b" ".join(overrides))
7675 % (name, b" ".join(overrides))
7672 )
7676 )
7673 table.update(cmdtable)
7677 table.update(cmdtable)
@@ -1,823 +1,823 b''
1 $ hg init basic
1 $ hg init basic
2 $ cd basic
2 $ cd basic
3
3
4 should complain
4 should complain
5
5
6 $ hg backout
6 $ hg backout
7 abort: please specify a revision to backout
7 abort: please specify a revision to backout
8 [255]
8 [255]
9 $ hg backout -r 0 0
9 $ hg backout -r 0 0
10 abort: please specify just one revision
10 abort: please specify just one revision
11 [255]
11 [255]
12
12
13 basic operation
13 basic operation
14 (this also tests that editor is invoked if the commit message is not
14 (this also tests that editor is invoked if the commit message is not
15 specified explicitly)
15 specified explicitly)
16
16
17 $ echo a > a
17 $ echo a > a
18 $ hg commit -d '0 0' -A -m a
18 $ hg commit -d '0 0' -A -m a
19 adding a
19 adding a
20 $ echo b >> a
20 $ echo b >> a
21 $ hg commit -d '1 0' -m b
21 $ hg commit -d '1 0' -m b
22
22
23 $ hg status --rev tip --rev "tip^1"
23 $ hg status --rev tip --rev "tip^1"
24 M a
24 M a
25 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true
25 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true
26 reverting a
26 reverting a
27 Backed out changeset a820f4f40a57
27 Backed out changeset a820f4f40a57
28
28
29
29
30 HG: Enter commit message. Lines beginning with 'HG:' are removed.
30 HG: Enter commit message. Lines beginning with 'HG:' are removed.
31 HG: Leave message empty to abort commit.
31 HG: Leave message empty to abort commit.
32 HG: --
32 HG: --
33 HG: user: test
33 HG: user: test
34 HG: branch 'default'
34 HG: branch 'default'
35 HG: changed a
35 HG: changed a
36 changeset 2:2929462c3dff backs out changeset 1:a820f4f40a57
36 changeset 2:2929462c3dff backs out changeset 1:a820f4f40a57
37 $ cat a
37 $ cat a
38 a
38 a
39 $ hg summary
39 $ hg summary
40 parent: 2:2929462c3dff tip
40 parent: 2:2929462c3dff tip
41 Backed out changeset a820f4f40a57
41 Backed out changeset a820f4f40a57
42 branch: default
42 branch: default
43 commit: (clean)
43 commit: (clean)
44 update: (current)
44 update: (current)
45 phases: 3 draft
45 phases: 3 draft
46
46
47 commit option
47 commit option
48
48
49 $ cd ..
49 $ cd ..
50 $ hg init commit
50 $ hg init commit
51 $ cd commit
51 $ cd commit
52
52
53 $ echo tomatoes > a
53 $ echo tomatoes > a
54 $ hg add a
54 $ hg add a
55 $ hg commit -d '0 0' -m tomatoes
55 $ hg commit -d '0 0' -m tomatoes
56
56
57 $ echo chair > b
57 $ echo chair > b
58 $ hg add b
58 $ hg add b
59 $ hg commit -d '1 0' -m chair
59 $ hg commit -d '1 0' -m chair
60
60
61 $ echo grapes >> a
61 $ echo grapes >> a
62 $ hg commit -d '2 0' -m grapes
62 $ hg commit -d '2 0' -m grapes
63
63
64 $ hg backout -d '4 0' 1 --tool=:fail
64 $ hg backout -d '4 0' 1 --tool=:fail
65 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
65 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 changeset 3:1c2161e97c0a backs out changeset 1:22cb4f70d813
66 changeset 3:1c2161e97c0a backs out changeset 1:22cb4f70d813
67 $ hg summary
67 $ hg summary
68 parent: 3:1c2161e97c0a tip
68 parent: 3:1c2161e97c0a tip
69 Backed out changeset 22cb4f70d813
69 Backed out changeset 22cb4f70d813
70 branch: default
70 branch: default
71 commit: (clean)
71 commit: (clean)
72 update: (current)
72 update: (current)
73 phases: 4 draft
73 phases: 4 draft
74
74
75 $ echo ypples > a
75 $ echo ypples > a
76 $ hg commit -d '5 0' -m ypples
76 $ hg commit -d '5 0' -m ypples
77
77
78 $ hg backout -d '6 0' 2 --tool=:fail
78 $ hg backout -d '6 0' 2 --tool=:fail
79 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
79 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
80 use 'hg resolve' to retry unresolved file merges
80 use 'hg resolve' to retry unresolved file merges
81 [1]
81 [1]
82 $ hg summary
82 $ hg summary
83 parent: 4:ed99997b793d tip
83 parent: 4:ed99997b793d tip
84 ypples
84 ypples
85 branch: default
85 branch: default
86 commit: 1 unresolved (clean)
86 commit: 1 unresolved (clean)
87 update: (current)
87 update: (current)
88 phases: 5 draft
88 phases: 5 draft
89 $ hg log -G
89 $ hg log -G
90 @ changeset: 4:ed99997b793d
90 @ changeset: 4:ed99997b793d
91 | tag: tip
91 | tag: tip
92 | user: test
92 | user: test
93 | date: Thu Jan 01 00:00:05 1970 +0000
93 | date: Thu Jan 01 00:00:05 1970 +0000
94 | summary: ypples
94 | summary: ypples
95 |
95 |
96 o changeset: 3:1c2161e97c0a
96 o changeset: 3:1c2161e97c0a
97 | user: test
97 | user: test
98 | date: Thu Jan 01 00:00:04 1970 +0000
98 | date: Thu Jan 01 00:00:04 1970 +0000
99 | summary: Backed out changeset 22cb4f70d813
99 | summary: Backed out changeset 22cb4f70d813
100 |
100 |
101 o changeset: 2:a8c6e511cfee
101 o changeset: 2:a8c6e511cfee
102 | user: test
102 | user: test
103 | date: Thu Jan 01 00:00:02 1970 +0000
103 | date: Thu Jan 01 00:00:02 1970 +0000
104 | summary: grapes
104 | summary: grapes
105 |
105 |
106 % changeset: 1:22cb4f70d813
106 % changeset: 1:22cb4f70d813
107 | user: test
107 | user: test
108 | date: Thu Jan 01 00:00:01 1970 +0000
108 | date: Thu Jan 01 00:00:01 1970 +0000
109 | summary: chair
109 | summary: chair
110 |
110 |
111 o changeset: 0:a5cb2dde5805
111 o changeset: 0:a5cb2dde5805
112 user: test
112 user: test
113 date: Thu Jan 01 00:00:00 1970 +0000
113 date: Thu Jan 01 00:00:00 1970 +0000
114 summary: tomatoes
114 summary: tomatoes
115
115
116
116
117 file that was removed is recreated
117 file that was removed is recreated
118 (this also tests that editor is not invoked if the commit message is
118 (this also tests that editor is not invoked if the commit message is
119 specified explicitly)
119 specified explicitly)
120
120
121 $ cd ..
121 $ cd ..
122 $ hg init remove
122 $ hg init remove
123 $ cd remove
123 $ cd remove
124
124
125 $ echo content > a
125 $ echo content > a
126 $ hg commit -d '0 0' -A -m a
126 $ hg commit -d '0 0' -A -m a
127 adding a
127 adding a
128
128
129 $ hg rm a
129 $ hg rm a
130 $ hg commit -d '1 0' -m b
130 $ hg commit -d '1 0' -m b
131
131
132 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true -m "Backed out changeset 76862dcce372"
132 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true -m "Backed out changeset 76862dcce372"
133 adding a
133 adding a
134 changeset 2:de31bdc76c0d backs out changeset 1:76862dcce372
134 changeset 2:de31bdc76c0d backs out changeset 1:76862dcce372
135 $ cat a
135 $ cat a
136 content
136 content
137 $ hg summary
137 $ hg summary
138 parent: 2:de31bdc76c0d tip
138 parent: 2:de31bdc76c0d tip
139 Backed out changeset 76862dcce372
139 Backed out changeset 76862dcce372
140 branch: default
140 branch: default
141 commit: (clean)
141 commit: (clean)
142 update: (current)
142 update: (current)
143 phases: 3 draft
143 phases: 3 draft
144
144
145 backout of backout is as if nothing happened
145 backout of backout is as if nothing happened
146
146
147 $ hg backout -d '3 0' --merge tip --tool=true
147 $ hg backout -d '3 0' --merge tip --tool=true
148 removing a
148 removing a
149 changeset 3:7f6d0f120113 backs out changeset 2:de31bdc76c0d
149 changeset 3:7f6d0f120113 backs out changeset 2:de31bdc76c0d
150 $ test -f a
150 $ test -f a
151 [1]
151 [1]
152 $ hg summary
152 $ hg summary
153 parent: 3:7f6d0f120113 tip
153 parent: 3:7f6d0f120113 tip
154 Backed out changeset de31bdc76c0d
154 Backed out changeset de31bdc76c0d
155 branch: default
155 branch: default
156 commit: (clean)
156 commit: (clean)
157 update: (current)
157 update: (current)
158 phases: 4 draft
158 phases: 4 draft
159
159
160 Test that 'hg rollback' restores dirstate just before opening
160 Test that 'hg rollback' restores dirstate just before opening
161 transaction: in-memory dirstate changes should be written into
161 transaction: in-memory dirstate changes should be written into
162 '.hg/journal.dirstate' as expected.
162 '.hg/journal.dirstate' as expected.
163
163
164 $ echo 'removed soon' > b
164 $ echo 'removed soon' > b
165 $ hg commit -A -d '4 0' -m 'prepare for subsequent removing'
165 $ hg commit -A -d '4 0' -m 'prepare for subsequent removing'
166 adding b
166 adding b
167 $ echo 'newly added' > c
167 $ echo 'newly added' > c
168 $ hg add c
168 $ hg add c
169 $ hg remove b
169 $ hg remove b
170 $ hg commit -d '5 0' -m 'prepare for subsequent backout'
170 $ hg commit -d '5 0' -m 'prepare for subsequent backout'
171 $ touch -t 200001010000 c
171 $ touch -t 200001010000 c
172 $ hg status -A
172 $ hg status -A
173 C c
173 C c
174 $ hg debugstate --no-dates
174 $ hg debugstate --no-dates
175 n 644 12 set c
175 n 644 12 set c
176 $ hg backout -d '6 0' -m 'to be rollback-ed soon' -r .
176 $ hg backout -d '6 0' -m 'to be rollback-ed soon' -r .
177 removing c
177 removing c
178 adding b
178 adding b
179 changeset 6:4bfec048029d backs out changeset 5:fac0b729a654
179 changeset 6:4bfec048029d backs out changeset 5:fac0b729a654
180 $ hg rollback -q
180 $ hg rollback -q
181 $ hg status -A
181 $ hg status -A
182 A b
182 A b
183 R c
183 R c
184 $ hg debugstate --no-dates
184 $ hg debugstate --no-dates
185 a 0 -1 unset b
185 a 0 -1 unset b
186 r 0 0 set c
186 r 0 0 set c
187
187
188 across branch
188 across branch
189
189
190 $ cd ..
190 $ cd ..
191 $ hg init branch
191 $ hg init branch
192 $ cd branch
192 $ cd branch
193 $ echo a > a
193 $ echo a > a
194 $ hg ci -Am0
194 $ hg ci -Am0
195 adding a
195 adding a
196 $ echo b > b
196 $ echo b > b
197 $ hg ci -Am1
197 $ hg ci -Am1
198 adding b
198 adding b
199 $ hg co -C 0
199 $ hg co -C 0
200 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
200 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
201 $ hg summary
201 $ hg summary
202 parent: 0:f7b1eb17ad24
202 parent: 0:f7b1eb17ad24
203 0
203 0
204 branch: default
204 branch: default
205 commit: (clean)
205 commit: (clean)
206 update: 1 new changesets (update)
206 update: 1 new changesets (update)
207 phases: 2 draft
207 phases: 2 draft
208
208
209 should fail
209 should fail
210
210
211 $ hg backout 1
211 $ hg backout 1
212 abort: cannot backout change that is not an ancestor
212 abort: cannot backout change that is not an ancestor
213 [255]
213 [255]
214 $ echo c > c
214 $ echo c > c
215 $ hg ci -Am2
215 $ hg ci -Am2
216 adding c
216 adding c
217 created new head
217 created new head
218 $ hg summary
218 $ hg summary
219 parent: 2:db815d6d32e6 tip
219 parent: 2:db815d6d32e6 tip
220 2
220 2
221 branch: default
221 branch: default
222 commit: (clean)
222 commit: (clean)
223 update: 1 new changesets, 2 branch heads (merge)
223 update: 1 new changesets, 2 branch heads (merge)
224 phases: 3 draft
224 phases: 3 draft
225
225
226 should fail
226 should fail
227
227
228 $ hg backout 1
228 $ hg backout 1
229 abort: cannot backout change that is not an ancestor
229 abort: cannot backout change that is not an ancestor
230 [255]
230 [255]
231 $ hg summary
231 $ hg summary
232 parent: 2:db815d6d32e6 tip
232 parent: 2:db815d6d32e6 tip
233 2
233 2
234 branch: default
234 branch: default
235 commit: (clean)
235 commit: (clean)
236 update: 1 new changesets, 2 branch heads (merge)
236 update: 1 new changesets, 2 branch heads (merge)
237 phases: 3 draft
237 phases: 3 draft
238
238
239 backout with merge
239 backout with merge
240
240
241 $ cd ..
241 $ cd ..
242 $ hg init merge
242 $ hg init merge
243 $ cd merge
243 $ cd merge
244
244
245 $ echo line 1 > a
245 $ echo line 1 > a
246 $ echo line 2 >> a
246 $ echo line 2 >> a
247 $ hg commit -d '0 0' -A -m a
247 $ hg commit -d '0 0' -A -m a
248 adding a
248 adding a
249 $ hg summary
249 $ hg summary
250 parent: 0:59395513a13a tip
250 parent: 0:59395513a13a tip
251 a
251 a
252 branch: default
252 branch: default
253 commit: (clean)
253 commit: (clean)
254 update: (current)
254 update: (current)
255 phases: 1 draft
255 phases: 1 draft
256
256
257 remove line 1
257 remove line 1
258
258
259 $ echo line 2 > a
259 $ echo line 2 > a
260 $ hg commit -d '1 0' -m b
260 $ hg commit -d '1 0' -m b
261
261
262 $ echo line 3 >> a
262 $ echo line 3 >> a
263 $ hg commit -d '2 0' -m c
263 $ hg commit -d '2 0' -m c
264
264
265 $ hg backout --merge -d '3 0' 1 --tool=true
265 $ hg backout --merge -d '3 0' 1 --tool=true
266 reverting a
266 reverting a
267 created new head
267 created new head
268 changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182
268 changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182
269 merging with changeset 3:26b8ccb9ad91
269 merging with changeset 3:26b8ccb9ad91
270 merging a
270 merging a
271 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
271 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
272 (branch merge, don't forget to commit)
272 (branch merge, don't forget to commit)
273 $ hg commit -d '4 0' -m d
273 $ hg commit -d '4 0' -m d
274 $ hg summary
274 $ hg summary
275 parent: 4:c7df5e0b9c09 tip
275 parent: 4:c7df5e0b9c09 tip
276 d
276 d
277 branch: default
277 branch: default
278 commit: (clean)
278 commit: (clean)
279 update: (current)
279 update: (current)
280 phases: 5 draft
280 phases: 5 draft
281
281
282 check line 1 is back
282 check line 1 is back
283
283
284 $ cat a
284 $ cat a
285 line 1
285 line 1
286 line 2
286 line 2
287 line 3
287 line 3
288
288
289 Test visibility of in-memory dirstate changes outside transaction to
289 Test visibility of in-memory dirstate changes outside transaction to
290 external hook process
290 external hook process
291
291
292 $ cat > $TESTTMP/checkvisibility.sh <<EOF
292 $ cat > $TESTTMP/checkvisibility.sh <<EOF
293 > echo "==== \$1:"
293 > echo "==== \$1:"
294 > hg parents --template "{rev}:{node|short}\n"
294 > hg parents --template "{rev}:{node|short}\n"
295 > echo "===="
295 > echo "===="
296 > EOF
296 > EOF
297
297
298 "hg backout --merge REV1" at REV2 below implies steps below:
298 "hg backout --merge REV1" at REV2 below implies steps below:
299
299
300 (1) update to REV1 (REV2 => REV1)
300 (1) update to REV1 (REV2 => REV1)
301 (2) revert by REV1^1
301 (2) revert by REV1^1
302 (3) commit backing out revision (REV3)
302 (3) commit backing out revision (REV3)
303 (4) update to REV2 (REV3 => REV2)
303 (4) update to REV2 (REV3 => REV2)
304 (5) merge with REV3 (REV2 => REV2, REV3)
304 (5) merge with REV3 (REV2 => REV2, REV3)
305
305
306 == test visibility to external preupdate hook
306 == test visibility to external preupdate hook
307
307
308 $ hg update -q -C 2
308 $ hg update -q -C 2
309 $ hg --config extensions.strip= strip 3
309 $ hg --config extensions.strip= strip 3
310 saved backup bundle to * (glob)
310 saved backup bundle to * (glob)
311
311
312 $ cat >> .hg/hgrc <<EOF
312 $ cat >> .hg/hgrc <<EOF
313 > [hooks]
313 > [hooks]
314 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
314 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
315 > EOF
315 > EOF
316
316
317 ("-m" is needed to avoid writing dirstate changes out at other than
317 ("-m" is needed to avoid writing dirstate changes out at other than
318 invocation of the hook to be examined)
318 invocation of the hook to be examined)
319
319
320 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
320 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
321 ==== preupdate:
321 ==== preupdate:
322 2:6ea3f2a197a2
322 2:6ea3f2a197a2
323 ====
323 ====
324 reverting a
324 reverting a
325 created new head
325 created new head
326 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
326 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
327 ==== preupdate:
327 ==== preupdate:
328 3:d92a3f57f067
328 3:d92a3f57f067
329 ====
329 ====
330 merging with changeset 3:d92a3f57f067
330 merging with changeset 3:d92a3f57f067
331 ==== preupdate:
331 ==== preupdate:
332 2:6ea3f2a197a2
332 2:6ea3f2a197a2
333 ====
333 ====
334 merging a
334 merging a
335 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
335 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
336 (branch merge, don't forget to commit)
336 (branch merge, don't forget to commit)
337
337
338 $ cat >> .hg/hgrc <<EOF
338 $ cat >> .hg/hgrc <<EOF
339 > [hooks]
339 > [hooks]
340 > preupdate.visibility =
340 > preupdate.visibility =
341 > EOF
341 > EOF
342
342
343 == test visibility to external update hook
343 == test visibility to external update hook
344
344
345 $ hg update -q -C 2
345 $ hg update -q -C 2
346 $ hg --config extensions.strip= strip 3
346 $ hg --config extensions.strip= strip 3
347 saved backup bundle to * (glob)
347 saved backup bundle to * (glob)
348
348
349 $ cat >> .hg/hgrc <<EOF
349 $ cat >> .hg/hgrc <<EOF
350 > [hooks]
350 > [hooks]
351 > update.visibility = sh $TESTTMP/checkvisibility.sh update
351 > update.visibility = sh $TESTTMP/checkvisibility.sh update
352 > EOF
352 > EOF
353
353
354 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
354 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
355 ==== update:
355 ==== update:
356 1:5a50a024c182
356 1:5a50a024c182
357 ====
357 ====
358 reverting a
358 reverting a
359 created new head
359 created new head
360 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
360 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
361 ==== update:
361 ==== update:
362 2:6ea3f2a197a2
362 2:6ea3f2a197a2
363 ====
363 ====
364 merging with changeset 3:d92a3f57f067
364 merging with changeset 3:d92a3f57f067
365 merging a
365 merging a
366 ==== update:
366 ==== update:
367 2:6ea3f2a197a2
367 2:6ea3f2a197a2
368 3:d92a3f57f067
368 3:d92a3f57f067
369 ====
369 ====
370 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
370 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
371 (branch merge, don't forget to commit)
371 (branch merge, don't forget to commit)
372
372
373 $ cat >> .hg/hgrc <<EOF
373 $ cat >> .hg/hgrc <<EOF
374 > [hooks]
374 > [hooks]
375 > update.visibility =
375 > update.visibility =
376 > EOF
376 > EOF
377
377
378 $ cd ..
378 $ cd ..
379
379
380 backout should not back out subsequent changesets
380 backout should not back out subsequent changesets
381
381
382 $ hg init onecs
382 $ hg init onecs
383 $ cd onecs
383 $ cd onecs
384 $ echo 1 > a
384 $ echo 1 > a
385 $ hg commit -d '0 0' -A -m a
385 $ hg commit -d '0 0' -A -m a
386 adding a
386 adding a
387 $ echo 2 >> a
387 $ echo 2 >> a
388 $ hg commit -d '1 0' -m b
388 $ hg commit -d '1 0' -m b
389 $ echo 1 > b
389 $ echo 1 > b
390 $ hg commit -d '2 0' -A -m c
390 $ hg commit -d '2 0' -A -m c
391 adding b
391 adding b
392 $ hg summary
392 $ hg summary
393 parent: 2:882396649954 tip
393 parent: 2:882396649954 tip
394 c
394 c
395 branch: default
395 branch: default
396 commit: (clean)
396 commit: (clean)
397 update: (current)
397 update: (current)
398 phases: 3 draft
398 phases: 3 draft
399
399
400 without --merge
400 without --merge
401 $ hg backout --no-commit -d '3 0' 1 --tool=true
401 $ hg backout --no-commit -d '3 0' 1 --tool=true
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 changeset 22bca4c721e5 backed out, don't forget to commit.
403 changeset 22bca4c721e5 backed out, don't forget to commit.
404 $ hg locate b
404 $ hg locate b
405 b
405 b
406 $ hg update -C tip
406 $ hg update -C tip
407 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
408 $ hg locate b
408 $ hg locate b
409 b
409 b
410 $ hg summary
410 $ hg summary
411 parent: 2:882396649954 tip
411 parent: 2:882396649954 tip
412 c
412 c
413 branch: default
413 branch: default
414 commit: (clean)
414 commit: (clean)
415 update: (current)
415 update: (current)
416 phases: 3 draft
416 phases: 3 draft
417
417
418 with --merge
418 with --merge
419 $ hg backout --merge -d '3 0' 1 --tool=true
419 $ hg backout --merge -d '3 0' 1 --tool=true
420 reverting a
420 reverting a
421 created new head
421 created new head
422 changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5
422 changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5
423 merging with changeset 3:3202beb76721
423 merging with changeset 3:3202beb76721
424 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 (branch merge, don't forget to commit)
425 (branch merge, don't forget to commit)
426 $ hg locate b
426 $ hg locate b
427 b
427 b
428 $ hg update -C tip
428 $ hg update -C tip
429 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
429 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
430 $ hg locate b
430 $ hg locate b
431 [1]
431 [1]
432
432
433 $ cd ..
433 $ cd ..
434 $ hg init m
434 $ hg init m
435 $ cd m
435 $ cd m
436 $ echo a > a
436 $ echo a > a
437 $ hg commit -d '0 0' -A -m a
437 $ hg commit -d '0 0' -A -m a
438 adding a
438 adding a
439 $ echo b > b
439 $ echo b > b
440 $ hg commit -d '1 0' -A -m b
440 $ hg commit -d '1 0' -A -m b
441 adding b
441 adding b
442 $ echo c > c
442 $ echo c > c
443 $ hg commit -d '2 0' -A -m b
443 $ hg commit -d '2 0' -A -m b
444 adding c
444 adding c
445 $ hg update 1
445 $ hg update 1
446 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
446 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
447 $ echo d > d
447 $ echo d > d
448 $ hg commit -d '3 0' -A -m c
448 $ hg commit -d '3 0' -A -m c
449 adding d
449 adding d
450 created new head
450 created new head
451 $ hg merge 2
451 $ hg merge 2
452 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
452 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 (branch merge, don't forget to commit)
453 (branch merge, don't forget to commit)
454 $ hg commit -d '4 0' -A -m d
454 $ hg commit -d '4 0' -A -m d
455 $ hg summary
455 $ hg summary
456 parent: 4:b2f3bb92043e tip
456 parent: 4:b2f3bb92043e tip
457 d
457 d
458 branch: default
458 branch: default
459 commit: (clean)
459 commit: (clean)
460 update: (current)
460 update: (current)
461 phases: 5 draft
461 phases: 5 draft
462
462
463 backout of merge should fail
463 backout of merge should fail
464
464
465 $ hg backout 4
465 $ hg backout 4
466 abort: cannot backout a merge changeset
466 abort: cannot backout a merge changeset
467 [255]
467 [255]
468
468
469 backout of merge with bad parent should fail
469 backout of merge with bad parent should fail
470
470
471 $ hg backout --parent 0 4
471 $ hg backout --parent 0 4
472 abort: cb9a9f314b8b is not a parent of b2f3bb92043e
472 abort: cb9a9f314b8b is not a parent of b2f3bb92043e
473 [255]
473 [255]
474
474
475 backout of non-merge with parent should fail
475 backout of non-merge with parent should fail
476
476
477 $ hg backout --parent 0 3
477 $ hg backout --parent 0 3
478 abort: cannot use --parent on non-merge changeset
478 abort: cannot use --parent on non-merge changeset
479 [255]
479 [255]
480
480
481 backout with valid parent should be ok
481 backout with valid parent should be ok
482
482
483 $ hg backout -d '5 0' --parent 2 4 --tool=true
483 $ hg backout -d '5 0' --parent 2 4 --tool=true
484 removing d
484 removing d
485 changeset 5:10e5328c8435 backs out changeset 4:b2f3bb92043e
485 changeset 5:10e5328c8435 backs out changeset 4:b2f3bb92043e
486 $ hg summary
486 $ hg summary
487 parent: 5:10e5328c8435 tip
487 parent: 5:10e5328c8435 tip
488 Backed out changeset b2f3bb92043e
488 Backed out changeset b2f3bb92043e
489 branch: default
489 branch: default
490 commit: (clean)
490 commit: (clean)
491 update: (current)
491 update: (current)
492 phases: 6 draft
492 phases: 6 draft
493
493
494 $ hg rollback
494 $ hg rollback
495 repository tip rolled back to revision 4 (undo commit)
495 repository tip rolled back to revision 4 (undo commit)
496 working directory now based on revision 4
496 working directory now based on revision 4
497 $ hg update -C
497 $ hg update -C
498 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
498 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
499 $ hg summary
499 $ hg summary
500 parent: 4:b2f3bb92043e tip
500 parent: 4:b2f3bb92043e tip
501 d
501 d
502 branch: default
502 branch: default
503 commit: (clean)
503 commit: (clean)
504 update: (current)
504 update: (current)
505 phases: 5 draft
505 phases: 5 draft
506
506
507 $ hg backout -d '6 0' --parent 3 4 --tool=true
507 $ hg backout -d '6 0' --parent 3 4 --tool=true
508 removing c
508 removing c
509 changeset 5:033590168430 backs out changeset 4:b2f3bb92043e
509 changeset 5:033590168430 backs out changeset 4:b2f3bb92043e
510 $ hg summary
510 $ hg summary
511 parent: 5:033590168430 tip
511 parent: 5:033590168430 tip
512 Backed out changeset b2f3bb92043e
512 Backed out changeset b2f3bb92043e
513 branch: default
513 branch: default
514 commit: (clean)
514 commit: (clean)
515 update: (current)
515 update: (current)
516 phases: 6 draft
516 phases: 6 draft
517
517
518 $ cd ..
518 $ cd ..
519
519
520 named branches
520 named branches
521
521
522 $ hg init named_branches
522 $ hg init named_branches
523 $ cd named_branches
523 $ cd named_branches
524
524
525 $ echo default > default
525 $ echo default > default
526 $ hg ci -d '0 0' -Am default
526 $ hg ci -d '0 0' -Am default
527 adding default
527 adding default
528 $ hg branch branch1
528 $ hg branch branch1
529 marked working directory as branch branch1
529 marked working directory as branch branch1
530 (branches are permanent and global, did you want a bookmark?)
530 (branches are permanent and global, did you want a bookmark?)
531 $ echo branch1 > file1
531 $ echo branch1 > file1
532 $ hg ci -d '1 0' -Am file1
532 $ hg ci -d '1 0' -Am file1
533 adding file1
533 adding file1
534 $ hg branch branch2
534 $ hg branch branch2
535 marked working directory as branch branch2
535 marked working directory as branch branch2
536 $ echo branch2 > file2
536 $ echo branch2 > file2
537 $ hg ci -d '2 0' -Am file2
537 $ hg ci -d '2 0' -Am file2
538 adding file2
538 adding file2
539
539
540 without --merge
540 without --merge
541 $ hg backout --no-commit -r 1 --tool=true
541 $ hg backout --no-commit -r 1 --tool=true
542 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
542 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
543 changeset bf1602f437f3 backed out, don't forget to commit.
543 changeset bf1602f437f3 backed out, don't forget to commit.
544 $ hg branch
544 $ hg branch
545 branch2
545 branch2
546 $ hg status -A
546 $ hg status -A
547 R file1
547 R file1
548 C default
548 C default
549 C file2
549 C file2
550 $ hg summary
550 $ hg summary
551 parent: 2:45bbcd363bf0 tip
551 parent: 2:45bbcd363bf0 tip
552 file2
552 file2
553 branch: branch2
553 branch: branch2
554 commit: 1 removed
554 commit: 1 removed
555 update: (current)
555 update: (current)
556 phases: 3 draft
556 phases: 3 draft
557
557
558 with --merge
558 with --merge
559 (this also tests that editor is invoked if '--edit' is specified
559 (this also tests that editor is invoked if '--edit' is specified
560 explicitly regardless of '--message')
560 explicitly regardless of '--message')
561
561
562 $ hg update -qC
562 $ hg update -qC
563 $ HGEDITOR=cat hg backout --merge -d '3 0' -r 1 -m 'backout on branch1' --tool=true --edit
563 $ HGEDITOR=cat hg backout --merge -d '3 0' -r 1 -m 'backout on branch1' --tool=true --edit
564 removing file1
564 removing file1
565 backout on branch1
565 backout on branch1
566
566
567
567
568 HG: Enter commit message. Lines beginning with 'HG:' are removed.
568 HG: Enter commit message. Lines beginning with 'HG:' are removed.
569 HG: Leave message empty to abort commit.
569 HG: Leave message empty to abort commit.
570 HG: --
570 HG: --
571 HG: user: test
571 HG: user: test
572 HG: branch 'branch2'
572 HG: branch 'branch2'
573 HG: removed file1
573 HG: removed file1
574 created new head
574 created new head
575 changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3
575 changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3
576 merging with changeset 3:d4e8f6db59fb
576 merging with changeset 3:d4e8f6db59fb
577 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
577 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
578 (branch merge, don't forget to commit)
578 (branch merge, don't forget to commit)
579 $ hg summary
579 $ hg summary
580 parent: 2:45bbcd363bf0
580 parent: 2:45bbcd363bf0
581 file2
581 file2
582 parent: 3:d4e8f6db59fb tip
582 parent: 3:d4e8f6db59fb tip
583 backout on branch1
583 backout on branch1
584 branch: branch2
584 branch: branch2
585 commit: 1 removed (merge)
585 commit: 1 removed (merge)
586 update: (current)
586 update: (current)
587 phases: 4 draft
587 phases: 4 draft
588 $ hg update -q -C 2
588 $ hg update -q -C 2
589
589
590 on branch2 with branch1 not merged, so file1 should still exist:
590 on branch2 with branch1 not merged, so file1 should still exist:
591
591
592 $ hg id
592 $ hg id
593 45bbcd363bf0 (branch2)
593 45bbcd363bf0 (branch2)
594 $ hg st -A
594 $ hg st -A
595 C default
595 C default
596 C file1
596 C file1
597 C file2
597 C file2
598 $ hg summary
598 $ hg summary
599 parent: 2:45bbcd363bf0
599 parent: 2:45bbcd363bf0
600 file2
600 file2
601 branch: branch2
601 branch: branch2
602 commit: (clean)
602 commit: (clean)
603 update: 1 new changesets, 2 branch heads (merge)
603 update: 1 new changesets, 2 branch heads (merge)
604 phases: 4 draft
604 phases: 4 draft
605
605
606 on branch2 with branch1 merged, so file1 should be gone:
606 on branch2 with branch1 merged, so file1 should be gone:
607
607
608 $ hg merge
608 $ hg merge
609 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
609 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
610 (branch merge, don't forget to commit)
610 (branch merge, don't forget to commit)
611 $ hg ci -d '4 0' -m 'merge backout of branch1'
611 $ hg ci -d '4 0' -m 'merge backout of branch1'
612 $ hg id
612 $ hg id
613 d97a8500a969 (branch2) tip
613 d97a8500a969 (branch2) tip
614 $ hg st -A
614 $ hg st -A
615 C default
615 C default
616 C file2
616 C file2
617 $ hg summary
617 $ hg summary
618 parent: 4:d97a8500a969 tip
618 parent: 4:d97a8500a969 tip
619 merge backout of branch1
619 merge backout of branch1
620 branch: branch2
620 branch: branch2
621 commit: (clean)
621 commit: (clean)
622 update: (current)
622 update: (current)
623 phases: 5 draft
623 phases: 5 draft
624
624
625 on branch1, so no file1 and file2:
625 on branch1, so no file1 and file2:
626
626
627 $ hg co -C branch1
627 $ hg co -C branch1
628 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
628 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
629 $ hg id
629 $ hg id
630 bf1602f437f3 (branch1)
630 bf1602f437f3 (branch1)
631 $ hg st -A
631 $ hg st -A
632 C default
632 C default
633 C file1
633 C file1
634 $ hg summary
634 $ hg summary
635 parent: 1:bf1602f437f3
635 parent: 1:bf1602f437f3
636 file1
636 file1
637 branch: branch1
637 branch: branch1
638 commit: (clean)
638 commit: (clean)
639 update: (current)
639 update: (current)
640 phases: 5 draft
640 phases: 5 draft
641
641
642 $ cd ..
642 $ cd ..
643
643
644 backout of empty changeset (issue4190)
644 backout of empty changeset (issue4190)
645
645
646 $ hg init emptycommit
646 $ hg init emptycommit
647 $ cd emptycommit
647 $ cd emptycommit
648
648
649 $ touch file1
649 $ touch file1
650 $ hg ci -Aqm file1
650 $ hg ci -Aqm file1
651 $ hg branch -q branch1
651 $ hg branch -q branch1
652 $ hg ci -qm branch1
652 $ hg ci -qm branch1
653 $ hg backout -v 1
653 $ hg backout -v 1
654 resolving manifests
654 resolving manifests
655 nothing changed
655 nothing changed
656 [1]
656 [1]
657
657
658 $ cd ..
658 $ cd ..
659
659
660
660
661 Test usage of `hg resolve` in case of conflict
661 Test usage of `hg resolve` in case of conflict
662 (issue4163)
662 (issue4163)
663
663
664 $ hg init issue4163
664 $ hg init issue4163
665 $ cd issue4163
665 $ cd issue4163
666 $ touch foo
666 $ touch foo
667 $ hg add foo
667 $ hg add foo
668 $ cat > foo << EOF
668 $ cat > foo << EOF
669 > one
669 > one
670 > two
670 > two
671 > three
671 > three
672 > four
672 > four
673 > five
673 > five
674 > six
674 > six
675 > seven
675 > seven
676 > height
676 > height
677 > nine
677 > nine
678 > ten
678 > ten
679 > EOF
679 > EOF
680 $ hg ci -m 'initial'
680 $ hg ci -m 'initial'
681 $ cat > foo << EOF
681 $ cat > foo << EOF
682 > one
682 > one
683 > two
683 > two
684 > THREE
684 > THREE
685 > four
685 > four
686 > five
686 > five
687 > six
687 > six
688 > seven
688 > seven
689 > height
689 > height
690 > nine
690 > nine
691 > ten
691 > ten
692 > EOF
692 > EOF
693 $ hg ci -m 'capital three'
693 $ hg ci -m 'capital three'
694 $ cat > foo << EOF
694 $ cat > foo << EOF
695 > one
695 > one
696 > two
696 > two
697 > THREE
697 > THREE
698 > four
698 > four
699 > five
699 > five
700 > six
700 > six
701 > seven
701 > seven
702 > height
702 > height
703 > nine
703 > nine
704 > TEN
704 > TEN
705 > EOF
705 > EOF
706 $ hg ci -m 'capital ten'
706 $ hg ci -m 'capital ten'
707 $ hg backout -r 'desc("capital three")' --tool internal:fail
707 $ hg backout -r 'desc("capital three")' --tool internal:fail
708 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
708 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
709 use 'hg resolve' to retry unresolved file merges
709 use 'hg resolve' to retry unresolved file merges
710 [1]
710 [1]
711 $ hg status
711 $ hg status
712 $ hg debugmergestate -v
712 $ hg debugmergestate -v
713 v1 and v2 states match: using v2
713 v1 and v2 states match: using v2
714 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
714 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
715 other: a30dd8addae3ce71b8667868478542bc417439e6
715 other: a30dd8addae3ce71b8667868478542bc417439e6
716 file: foo (state "u")
716 file: foo (state "u")
717 local path: foo (hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, flags "")
717 local path: foo (hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, flags "")
718 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
718 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
719 other path: foo (node f50039b486d6fa1a90ae51778388cad161f425ee)
719 other path: foo (node f50039b486d6fa1a90ae51778388cad161f425ee)
720 extra: ancestorlinknode = 91360952243723bd5b1138d5f26bd8c8564cb553
720 extra: ancestorlinknode = 91360952243723bd5b1138d5f26bd8c8564cb553
721 $ mv .hg/merge/state2 .hg/merge/state2-moved
721 $ mv .hg/merge/state2 .hg/merge/state2-moved
722 $ hg debugmergestate -v
722 $ hg debugmergestate -v
723 no version 2 merge state
723 no version 2 merge state
724 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
724 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
725 other: b71750c4b0fdf719734971e3ef90dbeab5919a2d
725 other: b71750c4b0fdf719734971e3ef90dbeab5919a2d
726 file: foo (state "u")
726 file: foo (state "u")
727 local path: foo (hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, flags "")
727 local path: foo (hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, flags "")
728 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
728 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
729 other path: (node foo)
729 other path: (node foo)
730 $ mv .hg/merge/state2-moved .hg/merge/state2
730 $ mv .hg/merge/state2-moved .hg/merge/state2
731 $ hg resolve -l # still unresolved
731 $ hg resolve -l # still unresolved
732 U foo
732 U foo
733 $ hg summary
733 $ hg summary
734 parent: 2:b71750c4b0fd tip
734 parent: 2:b71750c4b0fd tip
735 capital ten
735 capital ten
736 branch: default
736 branch: default
737 commit: 1 unresolved (clean)
737 commit: 1 unresolved (clean)
738 update: (current)
738 update: (current)
739 phases: 3 draft
739 phases: 3 draft
740 $ hg log -G
740 $ hg log -G
741 @ changeset: 2:b71750c4b0fd
741 @ changeset: 2:b71750c4b0fd
742 | tag: tip
742 | tag: tip
743 | user: test
743 | user: test
744 | date: Thu Jan 01 00:00:00 1970 +0000
744 | date: Thu Jan 01 00:00:00 1970 +0000
745 | summary: capital ten
745 | summary: capital ten
746 |
746 |
747 o changeset: 1:913609522437
747 o changeset: 1:913609522437
748 | user: test
748 | user: test
749 | date: Thu Jan 01 00:00:00 1970 +0000
749 | date: Thu Jan 01 00:00:00 1970 +0000
750 | summary: capital three
750 | summary: capital three
751 |
751 |
752 % changeset: 0:a30dd8addae3
752 % changeset: 0:a30dd8addae3
753 user: test
753 user: test
754 date: Thu Jan 01 00:00:00 1970 +0000
754 date: Thu Jan 01 00:00:00 1970 +0000
755 summary: initial
755 summary: initial
756
756
757 $ hg resolve --all --debug
757 $ hg resolve --all --debug
758 picked tool ':merge' for foo (binary False symlink False changedelete False)
758 picked tool ':merge' for foo (binary False symlink False changedelete False)
759 merging foo
759 merging foo
760 my foo@b71750c4b0fd+ other foo@a30dd8addae3 ancestor foo@913609522437
760 my foo@b71750c4b0fd+ other foo@a30dd8addae3 ancestor foo@913609522437
761 premerge successful
761 premerge successful
762 (no more unresolved files)
762 (no more unresolved files)
763 continue: hg commit
763 continue: hg commit
764 $ hg status
764 $ hg status
765 M foo
765 M foo
766 ? foo.orig
766 ? foo.orig
767 $ hg resolve -l
767 $ hg resolve -l
768 R foo
768 R foo
769 $ hg summary
769 $ hg summary
770 parent: 2:b71750c4b0fd tip
770 parent: 2:b71750c4b0fd tip
771 capital ten
771 capital ten
772 branch: default
772 branch: default
773 commit: 1 modified, 1 unknown
773 commit: 1 modified, 1 unknown
774 update: (current)
774 update: (current)
775 phases: 3 draft
775 phases: 3 draft
776 $ cat foo
776 $ cat foo
777 one
777 one
778 two
778 two
779 three
779 three
780 four
780 four
781 five
781 five
782 six
782 six
783 seven
783 seven
784 height
784 height
785 nine
785 nine
786 TEN
786 TEN
787
787
788 --no-commit shouldn't commit
788 --no-commit shouldn't commit
789
789
790 $ hg init a
790 $ hg init a
791 $ cd a
791 $ cd a
792 $ for i in 1 2 3; do
792 $ for i in 1 2 3; do
793 > touch $i
793 > touch $i
794 > hg ci -Am $i
794 > hg ci -Am $i
795 > done
795 > done
796 adding 1
796 adding 1
797 adding 2
797 adding 2
798 adding 3
798 adding 3
799 $ hg backout --no-commit .
799 $ hg backout --no-commit .
800 removing 3
800 removing 3
801 changeset cccc23d9d68f backed out, don't forget to commit.
801 changeset cccc23d9d68f backed out, don't forget to commit.
802 $ hg revert -aq
802 $ hg revert -aq
803
803
804 --no-commit can't be used with --merge
804 --no-commit can't be used with --merge
805
805
806 $ hg backout --merge --no-commit 2
806 $ hg backout --merge --no-commit 2
807 abort: cannot specify both --no-commit and --merge
807 abort: cannot specify both --no-commit and --merge
808 [255]
808 [255]
809
809
810 Ensure that backout out the same changeset twice performs correctly:
810 Ensure that backout out the same changeset twice performs correctly:
811
811
812 $ hg backout 2
812 $ hg backout 2
813 removing 3
813 removing 3
814 changeset 3:8f188de730d9 backs out changeset 2:cccc23d9d68f
814 changeset 3:8f188de730d9 backs out changeset 2:cccc23d9d68f
815 $ echo 4 > 4
815 $ echo 4 > 4
816 $ hg ci -A -m 4
816 $ hg ci -A -m 4
817 adding 4
817 adding 4
818 $ hg up 2
818 $ hg up 2
819 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
819 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
820 $ hg backout 2
820 $ hg backout 2
821 removing 3
821 removing 3
822 created new head
822 warning: commit already existed in the repository!
823 changeset 3:8f188de730d9 backs out changeset 2:cccc23d9d68f
823 changeset 3:8f188de730d9 backs out changeset 2:cccc23d9d68f
@@ -1,2091 +1,2092 b''
1 test merge-tools configuration - mostly exercising filemerge.py
1 test merge-tools configuration - mostly exercising filemerge.py
2
2
3 $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test
3 $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [ui]
5 > [ui]
6 > merge=
6 > merge=
7 > [commands]
7 > [commands]
8 > merge.require-rev=True
8 > merge.require-rev=True
9 > EOF
9 > EOF
10 $ hg init repo
10 $ hg init repo
11 $ cd repo
11 $ cd repo
12
12
13 revision 0
13 revision 0
14
14
15 $ echo "revision 0" > f
15 $ echo "revision 0" > f
16 $ echo "space" >> f
16 $ echo "space" >> f
17 $ hg commit -Am "revision 0"
17 $ hg commit -Am "revision 0"
18 adding f
18 adding f
19
19
20 revision 1
20 revision 1
21
21
22 $ echo "revision 1" > f
22 $ echo "revision 1" > f
23 $ echo "space" >> f
23 $ echo "space" >> f
24 $ hg commit -Am "revision 1"
24 $ hg commit -Am "revision 1"
25 $ hg update 0 > /dev/null
25 $ hg update 0 > /dev/null
26
26
27 revision 2
27 revision 2
28
28
29 $ echo "revision 2" > f
29 $ echo "revision 2" > f
30 $ echo "space" >> f
30 $ echo "space" >> f
31 $ hg commit -Am "revision 2"
31 $ hg commit -Am "revision 2"
32 created new head
32 created new head
33 $ hg update 0 > /dev/null
33 $ hg update 0 > /dev/null
34
34
35 revision 3 - simple to merge
35 revision 3 - simple to merge
36
36
37 $ echo "revision 3" >> f
37 $ echo "revision 3" >> f
38 $ hg commit -Am "revision 3"
38 $ hg commit -Am "revision 3"
39 created new head
39 created new head
40
40
41 revision 4 - hard to merge
41 revision 4 - hard to merge
42
42
43 $ hg update 0 > /dev/null
43 $ hg update 0 > /dev/null
44 $ echo "revision 4" > f
44 $ echo "revision 4" > f
45 $ hg commit -Am "revision 4"
45 $ hg commit -Am "revision 4"
46 created new head
46 created new head
47
47
48 $ echo "[merge-tools]" > .hg/hgrc
48 $ echo "[merge-tools]" > .hg/hgrc
49
49
50 $ beforemerge() {
50 $ beforemerge() {
51 > cat .hg/hgrc
51 > cat .hg/hgrc
52 > echo "# hg update -C 1"
52 > echo "# hg update -C 1"
53 > hg update -C 1 > /dev/null
53 > hg update -C 1 > /dev/null
54 > }
54 > }
55 $ aftermerge() {
55 $ aftermerge() {
56 > echo "# cat f"
56 > echo "# cat f"
57 > cat f
57 > cat f
58 > echo "# hg stat"
58 > echo "# hg stat"
59 > hg stat
59 > hg stat
60 > echo "# hg resolve --list"
60 > echo "# hg resolve --list"
61 > hg resolve --list
61 > hg resolve --list
62 > rm -f f.orig
62 > rm -f f.orig
63 > }
63 > }
64
64
65 Tool selection
65 Tool selection
66
66
67 default is internal merge:
67 default is internal merge:
68
68
69 $ beforemerge
69 $ beforemerge
70 [merge-tools]
70 [merge-tools]
71 # hg update -C 1
71 # hg update -C 1
72
72
73 hg merge -r 2
73 hg merge -r 2
74 override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
74 override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
75 running from a devel copy, not a temp installation
75 running from a devel copy, not a temp installation
76
76
77 $ PATH="/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
77 $ PATH="/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
78 merging f
78 merging f
79 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
79 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
80 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
80 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
81 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
81 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
82 [1]
82 [1]
83 $ aftermerge
83 $ aftermerge
84 # cat f
84 # cat f
85 <<<<<<< working copy: ef83787e2614 - test: revision 1
85 <<<<<<< working copy: ef83787e2614 - test: revision 1
86 revision 1
86 revision 1
87 =======
87 =======
88 revision 2
88 revision 2
89 >>>>>>> merge rev: 0185f4e0cf02 - test: revision 2
89 >>>>>>> merge rev: 0185f4e0cf02 - test: revision 2
90 space
90 space
91 # hg stat
91 # hg stat
92 M f
92 M f
93 ? f.orig
93 ? f.orig
94 # hg resolve --list
94 # hg resolve --list
95 U f
95 U f
96
96
97 simplest hgrc using false for merge:
97 simplest hgrc using false for merge:
98
98
99 $ echo "false.whatever=" >> .hg/hgrc
99 $ echo "false.whatever=" >> .hg/hgrc
100 $ beforemerge
100 $ beforemerge
101 [merge-tools]
101 [merge-tools]
102 false.whatever=
102 false.whatever=
103 # hg update -C 1
103 # hg update -C 1
104 $ hg merge -r 2
104 $ hg merge -r 2
105 merging f
105 merging f
106 merging f failed!
106 merging f failed!
107 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
107 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
108 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
108 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
109 [1]
109 [1]
110 $ aftermerge
110 $ aftermerge
111 # cat f
111 # cat f
112 revision 1
112 revision 1
113 space
113 space
114 # hg stat
114 # hg stat
115 M f
115 M f
116 ? f.orig
116 ? f.orig
117 # hg resolve --list
117 # hg resolve --list
118 U f
118 U f
119
119
120 #if unix-permissions
120 #if unix-permissions
121
121
122 unexecutable file in $PATH shouldn't be found:
122 unexecutable file in $PATH shouldn't be found:
123
123
124 $ echo "echo fail" > false
124 $ echo "echo fail" > false
125 $ hg up -qC 1
125 $ hg up -qC 1
126 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
126 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
127 merging f
127 merging f
128 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
128 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
129 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
129 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
130 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
130 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
131 [1]
131 [1]
132 $ rm false
132 $ rm false
133
133
134 #endif
134 #endif
135
135
136 executable directory in $PATH shouldn't be found:
136 executable directory in $PATH shouldn't be found:
137
137
138 $ mkdir false
138 $ mkdir false
139 $ hg up -qC 1
139 $ hg up -qC 1
140 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
140 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
141 merging f
141 merging f
142 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
142 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
143 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
143 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
144 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
144 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
145 [1]
145 [1]
146 $ rmdir false
146 $ rmdir false
147
147
148 true with higher .priority gets precedence:
148 true with higher .priority gets precedence:
149
149
150 $ echo "true.priority=1" >> .hg/hgrc
150 $ echo "true.priority=1" >> .hg/hgrc
151 $ beforemerge
151 $ beforemerge
152 [merge-tools]
152 [merge-tools]
153 false.whatever=
153 false.whatever=
154 true.priority=1
154 true.priority=1
155 # hg update -C 1
155 # hg update -C 1
156 $ hg merge -r 2
156 $ hg merge -r 2
157 merging f
157 merging f
158 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
158 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
159 (branch merge, don't forget to commit)
159 (branch merge, don't forget to commit)
160 $ aftermerge
160 $ aftermerge
161 # cat f
161 # cat f
162 revision 1
162 revision 1
163 space
163 space
164 # hg stat
164 # hg stat
165 M f
165 M f
166 # hg resolve --list
166 # hg resolve --list
167 R f
167 R f
168
168
169 unless lowered on command line:
169 unless lowered on command line:
170
170
171 $ beforemerge
171 $ beforemerge
172 [merge-tools]
172 [merge-tools]
173 false.whatever=
173 false.whatever=
174 true.priority=1
174 true.priority=1
175 # hg update -C 1
175 # hg update -C 1
176 $ hg merge -r 2 --config merge-tools.true.priority=-7
176 $ hg merge -r 2 --config merge-tools.true.priority=-7
177 merging f
177 merging f
178 merging f failed!
178 merging f failed!
179 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
179 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
180 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
180 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
181 [1]
181 [1]
182 $ aftermerge
182 $ aftermerge
183 # cat f
183 # cat f
184 revision 1
184 revision 1
185 space
185 space
186 # hg stat
186 # hg stat
187 M f
187 M f
188 ? f.orig
188 ? f.orig
189 # hg resolve --list
189 # hg resolve --list
190 U f
190 U f
191
191
192 or false set higher on command line:
192 or false set higher on command line:
193
193
194 $ beforemerge
194 $ beforemerge
195 [merge-tools]
195 [merge-tools]
196 false.whatever=
196 false.whatever=
197 true.priority=1
197 true.priority=1
198 # hg update -C 1
198 # hg update -C 1
199 $ hg merge -r 2 --config merge-tools.false.priority=117
199 $ hg merge -r 2 --config merge-tools.false.priority=117
200 merging f
200 merging f
201 merging f failed!
201 merging f failed!
202 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
202 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
203 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
203 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
204 [1]
204 [1]
205 $ aftermerge
205 $ aftermerge
206 # cat f
206 # cat f
207 revision 1
207 revision 1
208 space
208 space
209 # hg stat
209 # hg stat
210 M f
210 M f
211 ? f.orig
211 ? f.orig
212 # hg resolve --list
212 # hg resolve --list
213 U f
213 U f
214
214
215 or true set to disabled:
215 or true set to disabled:
216 $ beforemerge
216 $ beforemerge
217 [merge-tools]
217 [merge-tools]
218 false.whatever=
218 false.whatever=
219 true.priority=1
219 true.priority=1
220 # hg update -C 1
220 # hg update -C 1
221 $ hg merge -r 2 --config merge-tools.true.disabled=yes
221 $ hg merge -r 2 --config merge-tools.true.disabled=yes
222 merging f
222 merging f
223 merging f failed!
223 merging f failed!
224 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
224 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
225 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
225 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
226 [1]
226 [1]
227 $ aftermerge
227 $ aftermerge
228 # cat f
228 # cat f
229 revision 1
229 revision 1
230 space
230 space
231 # hg stat
231 # hg stat
232 M f
232 M f
233 ? f.orig
233 ? f.orig
234 # hg resolve --list
234 # hg resolve --list
235 U f
235 U f
236
236
237 or true.executable not found in PATH:
237 or true.executable not found in PATH:
238
238
239 $ beforemerge
239 $ beforemerge
240 [merge-tools]
240 [merge-tools]
241 false.whatever=
241 false.whatever=
242 true.priority=1
242 true.priority=1
243 # hg update -C 1
243 # hg update -C 1
244 $ hg merge -r 2 --config merge-tools.true.executable=nonexistentmergetool
244 $ hg merge -r 2 --config merge-tools.true.executable=nonexistentmergetool
245 merging f
245 merging f
246 merging f failed!
246 merging f failed!
247 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
247 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
248 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
248 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
249 [1]
249 [1]
250 $ aftermerge
250 $ aftermerge
251 # cat f
251 # cat f
252 revision 1
252 revision 1
253 space
253 space
254 # hg stat
254 # hg stat
255 M f
255 M f
256 ? f.orig
256 ? f.orig
257 # hg resolve --list
257 # hg resolve --list
258 U f
258 U f
259
259
260 or true.executable with bogus path:
260 or true.executable with bogus path:
261
261
262 $ beforemerge
262 $ beforemerge
263 [merge-tools]
263 [merge-tools]
264 false.whatever=
264 false.whatever=
265 true.priority=1
265 true.priority=1
266 # hg update -C 1
266 # hg update -C 1
267 $ hg merge -r 2 --config merge-tools.true.executable=/nonexistent/mergetool
267 $ hg merge -r 2 --config merge-tools.true.executable=/nonexistent/mergetool
268 merging f
268 merging f
269 merging f failed!
269 merging f failed!
270 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
270 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
271 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
271 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
272 [1]
272 [1]
273 $ aftermerge
273 $ aftermerge
274 # cat f
274 # cat f
275 revision 1
275 revision 1
276 space
276 space
277 # hg stat
277 # hg stat
278 M f
278 M f
279 ? f.orig
279 ? f.orig
280 # hg resolve --list
280 # hg resolve --list
281 U f
281 U f
282
282
283 but true.executable set to cat found in PATH works:
283 but true.executable set to cat found in PATH works:
284
284
285 $ echo "true.executable=cat" >> .hg/hgrc
285 $ echo "true.executable=cat" >> .hg/hgrc
286 $ beforemerge
286 $ beforemerge
287 [merge-tools]
287 [merge-tools]
288 false.whatever=
288 false.whatever=
289 true.priority=1
289 true.priority=1
290 true.executable=cat
290 true.executable=cat
291 # hg update -C 1
291 # hg update -C 1
292 $ hg merge -r 2
292 $ hg merge -r 2
293 merging f
293 merging f
294 revision 1
294 revision 1
295 space
295 space
296 revision 0
296 revision 0
297 space
297 space
298 revision 2
298 revision 2
299 space
299 space
300 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
300 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
301 (branch merge, don't forget to commit)
301 (branch merge, don't forget to commit)
302 $ aftermerge
302 $ aftermerge
303 # cat f
303 # cat f
304 revision 1
304 revision 1
305 space
305 space
306 # hg stat
306 # hg stat
307 M f
307 M f
308 # hg resolve --list
308 # hg resolve --list
309 R f
309 R f
310
310
311 and true.executable set to cat with path works:
311 and true.executable set to cat with path works:
312
312
313 $ beforemerge
313 $ beforemerge
314 [merge-tools]
314 [merge-tools]
315 false.whatever=
315 false.whatever=
316 true.priority=1
316 true.priority=1
317 true.executable=cat
317 true.executable=cat
318 # hg update -C 1
318 # hg update -C 1
319 $ hg merge -r 2 --config merge-tools.true.executable=cat
319 $ hg merge -r 2 --config merge-tools.true.executable=cat
320 merging f
320 merging f
321 revision 1
321 revision 1
322 space
322 space
323 revision 0
323 revision 0
324 space
324 space
325 revision 2
325 revision 2
326 space
326 space
327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
328 (branch merge, don't forget to commit)
328 (branch merge, don't forget to commit)
329 $ aftermerge
329 $ aftermerge
330 # cat f
330 # cat f
331 revision 1
331 revision 1
332 space
332 space
333 # hg stat
333 # hg stat
334 M f
334 M f
335 # hg resolve --list
335 # hg resolve --list
336 R f
336 R f
337
337
338 executable set to python script that succeeds:
338 executable set to python script that succeeds:
339
339
340 $ cat > "$TESTTMP/myworkingmerge.py" <<EOF
340 $ cat > "$TESTTMP/myworkingmerge.py" <<EOF
341 > def myworkingmergefn(ui, repo, args, **kwargs):
341 > def myworkingmergefn(ui, repo, args, **kwargs):
342 > return False
342 > return False
343 > EOF
343 > EOF
344 $ beforemerge
344 $ beforemerge
345 [merge-tools]
345 [merge-tools]
346 false.whatever=
346 false.whatever=
347 true.priority=1
347 true.priority=1
348 true.executable=cat
348 true.executable=cat
349 # hg update -C 1
349 # hg update -C 1
350 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:myworkingmergefn"
350 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:myworkingmergefn"
351 merging f
351 merging f
352 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
352 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
353 (branch merge, don't forget to commit)
353 (branch merge, don't forget to commit)
354 $ aftermerge
354 $ aftermerge
355 # cat f
355 # cat f
356 revision 1
356 revision 1
357 space
357 space
358 # hg stat
358 # hg stat
359 M f
359 M f
360 # hg resolve --list
360 # hg resolve --list
361 R f
361 R f
362
362
363 executable set to python script that fails:
363 executable set to python script that fails:
364
364
365 $ cat > "$TESTTMP/mybrokenmerge.py" <<EOF
365 $ cat > "$TESTTMP/mybrokenmerge.py" <<EOF
366 > def mybrokenmergefn(ui, repo, args, **kwargs):
366 > def mybrokenmergefn(ui, repo, args, **kwargs):
367 > ui.write(b"some fail message\n")
367 > ui.write(b"some fail message\n")
368 > return True
368 > return True
369 > EOF
369 > EOF
370 $ beforemerge
370 $ beforemerge
371 [merge-tools]
371 [merge-tools]
372 false.whatever=
372 false.whatever=
373 true.priority=1
373 true.priority=1
374 true.executable=cat
374 true.executable=cat
375 # hg update -C 1
375 # hg update -C 1
376 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/mybrokenmerge.py:mybrokenmergefn"
376 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/mybrokenmerge.py:mybrokenmergefn"
377 merging f
377 merging f
378 some fail message
378 some fail message
379 abort: $TESTTMP/mybrokenmerge.py hook failed
379 abort: $TESTTMP/mybrokenmerge.py hook failed
380 [255]
380 [255]
381 $ aftermerge
381 $ aftermerge
382 # cat f
382 # cat f
383 revision 1
383 revision 1
384 space
384 space
385 # hg stat
385 # hg stat
386 ? f.orig
386 ? f.orig
387 # hg resolve --list
387 # hg resolve --list
388 U f
388 U f
389
389
390 executable set to python script that is missing function:
390 executable set to python script that is missing function:
391
391
392 $ beforemerge
392 $ beforemerge
393 [merge-tools]
393 [merge-tools]
394 false.whatever=
394 false.whatever=
395 true.priority=1
395 true.priority=1
396 true.executable=cat
396 true.executable=cat
397 # hg update -C 1
397 # hg update -C 1
398 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:missingFunction"
398 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:missingFunction"
399 merging f
399 merging f
400 abort: $TESTTMP/myworkingmerge.py does not have function: missingFunction
400 abort: $TESTTMP/myworkingmerge.py does not have function: missingFunction
401 [255]
401 [255]
402 $ aftermerge
402 $ aftermerge
403 # cat f
403 # cat f
404 revision 1
404 revision 1
405 space
405 space
406 # hg stat
406 # hg stat
407 ? f.orig
407 ? f.orig
408 # hg resolve --list
408 # hg resolve --list
409 U f
409 U f
410
410
411 executable set to missing python script:
411 executable set to missing python script:
412
412
413 $ beforemerge
413 $ beforemerge
414 [merge-tools]
414 [merge-tools]
415 false.whatever=
415 false.whatever=
416 true.priority=1
416 true.priority=1
417 true.executable=cat
417 true.executable=cat
418 # hg update -C 1
418 # hg update -C 1
419 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/missingpythonscript.py:mergefn"
419 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/missingpythonscript.py:mergefn"
420 merging f
420 merging f
421 abort: loading python merge script failed: $TESTTMP/missingpythonscript.py
421 abort: loading python merge script failed: $TESTTMP/missingpythonscript.py
422 [255]
422 [255]
423 $ aftermerge
423 $ aftermerge
424 # cat f
424 # cat f
425 revision 1
425 revision 1
426 space
426 space
427 # hg stat
427 # hg stat
428 ? f.orig
428 ? f.orig
429 # hg resolve --list
429 # hg resolve --list
430 U f
430 U f
431
431
432 executable set to python script but callable function is missing:
432 executable set to python script but callable function is missing:
433
433
434 $ beforemerge
434 $ beforemerge
435 [merge-tools]
435 [merge-tools]
436 false.whatever=
436 false.whatever=
437 true.priority=1
437 true.priority=1
438 true.executable=cat
438 true.executable=cat
439 # hg update -C 1
439 # hg update -C 1
440 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py"
440 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py"
441 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py
441 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py
442 [255]
442 [255]
443 $ aftermerge
443 $ aftermerge
444 # cat f
444 # cat f
445 revision 1
445 revision 1
446 space
446 space
447 # hg stat
447 # hg stat
448 # hg resolve --list
448 # hg resolve --list
449 U f
449 U f
450
450
451 executable set to python script but callable function is empty string:
451 executable set to python script but callable function is empty string:
452
452
453 $ beforemerge
453 $ beforemerge
454 [merge-tools]
454 [merge-tools]
455 false.whatever=
455 false.whatever=
456 true.priority=1
456 true.priority=1
457 true.executable=cat
457 true.executable=cat
458 # hg update -C 1
458 # hg update -C 1
459 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:"
459 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:"
460 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py:
460 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py:
461 [255]
461 [255]
462 $ aftermerge
462 $ aftermerge
463 # cat f
463 # cat f
464 revision 1
464 revision 1
465 space
465 space
466 # hg stat
466 # hg stat
467 # hg resolve --list
467 # hg resolve --list
468 U f
468 U f
469
469
470 executable set to python script but callable function is missing and path contains colon:
470 executable set to python script but callable function is missing and path contains colon:
471
471
472 $ beforemerge
472 $ beforemerge
473 [merge-tools]
473 [merge-tools]
474 false.whatever=
474 false.whatever=
475 true.priority=1
475 true.priority=1
476 true.executable=cat
476 true.executable=cat
477 # hg update -C 1
477 # hg update -C 1
478 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/some:dir/myworkingmerge.py"
478 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/some:dir/myworkingmerge.py"
479 abort: invalid 'python:' syntax: python:$TESTTMP/some:dir/myworkingmerge.py
479 abort: invalid 'python:' syntax: python:$TESTTMP/some:dir/myworkingmerge.py
480 [255]
480 [255]
481 $ aftermerge
481 $ aftermerge
482 # cat f
482 # cat f
483 revision 1
483 revision 1
484 space
484 space
485 # hg stat
485 # hg stat
486 # hg resolve --list
486 # hg resolve --list
487 U f
487 U f
488
488
489 executable set to python script filename that contains spaces:
489 executable set to python script filename that contains spaces:
490
490
491 $ mkdir -p "$TESTTMP/my path"
491 $ mkdir -p "$TESTTMP/my path"
492 $ cat > "$TESTTMP/my path/my working merge with spaces in filename.py" <<EOF
492 $ cat > "$TESTTMP/my path/my working merge with spaces in filename.py" <<EOF
493 > def myworkingmergefn(ui, repo, args, **kwargs):
493 > def myworkingmergefn(ui, repo, args, **kwargs):
494 > return False
494 > return False
495 > EOF
495 > EOF
496 $ beforemerge
496 $ beforemerge
497 [merge-tools]
497 [merge-tools]
498 false.whatever=
498 false.whatever=
499 true.priority=1
499 true.priority=1
500 true.executable=cat
500 true.executable=cat
501 # hg update -C 1
501 # hg update -C 1
502 $ hg merge -r 2 --config "merge-tools.true.executable=python:$TESTTMP/my path/my working merge with spaces in filename.py:myworkingmergefn"
502 $ hg merge -r 2 --config "merge-tools.true.executable=python:$TESTTMP/my path/my working merge with spaces in filename.py:myworkingmergefn"
503 merging f
503 merging f
504 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
504 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
505 (branch merge, don't forget to commit)
505 (branch merge, don't forget to commit)
506 $ aftermerge
506 $ aftermerge
507 # cat f
507 # cat f
508 revision 1
508 revision 1
509 space
509 space
510 # hg stat
510 # hg stat
511 M f
511 M f
512 # hg resolve --list
512 # hg resolve --list
513 R f
513 R f
514
514
515 #if unix-permissions
515 #if unix-permissions
516
516
517 environment variables in true.executable are handled:
517 environment variables in true.executable are handled:
518
518
519 $ echo 'echo "custom merge tool"' > .hg/merge.sh
519 $ echo 'echo "custom merge tool"' > .hg/merge.sh
520 $ beforemerge
520 $ beforemerge
521 [merge-tools]
521 [merge-tools]
522 false.whatever=
522 false.whatever=
523 true.priority=1
523 true.priority=1
524 true.executable=cat
524 true.executable=cat
525 # hg update -C 1
525 # hg update -C 1
526 $ hg --config merge-tools.true.executable='sh' \
526 $ hg --config merge-tools.true.executable='sh' \
527 > --config merge-tools.true.args=.hg/merge.sh \
527 > --config merge-tools.true.args=.hg/merge.sh \
528 > merge -r 2
528 > merge -r 2
529 merging f
529 merging f
530 custom merge tool
530 custom merge tool
531 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
531 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
532 (branch merge, don't forget to commit)
532 (branch merge, don't forget to commit)
533 $ aftermerge
533 $ aftermerge
534 # cat f
534 # cat f
535 revision 1
535 revision 1
536 space
536 space
537 # hg stat
537 # hg stat
538 M f
538 M f
539 # hg resolve --list
539 # hg resolve --list
540 R f
540 R f
541
541
542 #endif
542 #endif
543
543
544 Tool selection and merge-patterns
544 Tool selection and merge-patterns
545
545
546 merge-patterns specifies new tool false:
546 merge-patterns specifies new tool false:
547
547
548 $ beforemerge
548 $ beforemerge
549 [merge-tools]
549 [merge-tools]
550 false.whatever=
550 false.whatever=
551 true.priority=1
551 true.priority=1
552 true.executable=cat
552 true.executable=cat
553 # hg update -C 1
553 # hg update -C 1
554 $ hg merge -r 2 --config merge-patterns.f=false
554 $ hg merge -r 2 --config merge-patterns.f=false
555 merging f
555 merging f
556 merging f failed!
556 merging f failed!
557 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
557 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
558 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
558 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
559 [1]
559 [1]
560 $ aftermerge
560 $ aftermerge
561 # cat f
561 # cat f
562 revision 1
562 revision 1
563 space
563 space
564 # hg stat
564 # hg stat
565 M f
565 M f
566 ? f.orig
566 ? f.orig
567 # hg resolve --list
567 # hg resolve --list
568 U f
568 U f
569
569
570 merge-patterns specifies executable not found in PATH and gets warning:
570 merge-patterns specifies executable not found in PATH and gets warning:
571
571
572 $ beforemerge
572 $ beforemerge
573 [merge-tools]
573 [merge-tools]
574 false.whatever=
574 false.whatever=
575 true.priority=1
575 true.priority=1
576 true.executable=cat
576 true.executable=cat
577 # hg update -C 1
577 # hg update -C 1
578 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool
578 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool
579 couldn't find merge tool true (for pattern f)
579 couldn't find merge tool true (for pattern f)
580 merging f
580 merging f
581 couldn't find merge tool true (for pattern f)
581 couldn't find merge tool true (for pattern f)
582 merging f failed!
582 merging f failed!
583 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
583 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
584 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
584 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
585 [1]
585 [1]
586 $ aftermerge
586 $ aftermerge
587 # cat f
587 # cat f
588 revision 1
588 revision 1
589 space
589 space
590 # hg stat
590 # hg stat
591 M f
591 M f
592 ? f.orig
592 ? f.orig
593 # hg resolve --list
593 # hg resolve --list
594 U f
594 U f
595
595
596 merge-patterns specifies executable with bogus path and gets warning:
596 merge-patterns specifies executable with bogus path and gets warning:
597
597
598 $ beforemerge
598 $ beforemerge
599 [merge-tools]
599 [merge-tools]
600 false.whatever=
600 false.whatever=
601 true.priority=1
601 true.priority=1
602 true.executable=cat
602 true.executable=cat
603 # hg update -C 1
603 # hg update -C 1
604 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexistent/mergetool
604 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexistent/mergetool
605 couldn't find merge tool true (for pattern f)
605 couldn't find merge tool true (for pattern f)
606 merging f
606 merging f
607 couldn't find merge tool true (for pattern f)
607 couldn't find merge tool true (for pattern f)
608 merging f failed!
608 merging f failed!
609 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
609 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
610 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
610 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
611 [1]
611 [1]
612 $ aftermerge
612 $ aftermerge
613 # cat f
613 # cat f
614 revision 1
614 revision 1
615 space
615 space
616 # hg stat
616 # hg stat
617 M f
617 M f
618 ? f.orig
618 ? f.orig
619 # hg resolve --list
619 # hg resolve --list
620 U f
620 U f
621
621
622 ui.merge overrules priority
622 ui.merge overrules priority
623
623
624 ui.merge specifies false:
624 ui.merge specifies false:
625
625
626 $ beforemerge
626 $ beforemerge
627 [merge-tools]
627 [merge-tools]
628 false.whatever=
628 false.whatever=
629 true.priority=1
629 true.priority=1
630 true.executable=cat
630 true.executable=cat
631 # hg update -C 1
631 # hg update -C 1
632 $ hg merge -r 2 --config ui.merge=false
632 $ hg merge -r 2 --config ui.merge=false
633 merging f
633 merging f
634 merging f failed!
634 merging f failed!
635 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
635 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
636 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
636 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
637 [1]
637 [1]
638 $ aftermerge
638 $ aftermerge
639 # cat f
639 # cat f
640 revision 1
640 revision 1
641 space
641 space
642 # hg stat
642 # hg stat
643 M f
643 M f
644 ? f.orig
644 ? f.orig
645 # hg resolve --list
645 # hg resolve --list
646 U f
646 U f
647
647
648 ui.merge specifies internal:fail:
648 ui.merge specifies internal:fail:
649
649
650 $ beforemerge
650 $ beforemerge
651 [merge-tools]
651 [merge-tools]
652 false.whatever=
652 false.whatever=
653 true.priority=1
653 true.priority=1
654 true.executable=cat
654 true.executable=cat
655 # hg update -C 1
655 # hg update -C 1
656 $ hg merge -r 2 --config ui.merge=internal:fail
656 $ hg merge -r 2 --config ui.merge=internal:fail
657 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
657 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
658 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
658 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
659 [1]
659 [1]
660 $ aftermerge
660 $ aftermerge
661 # cat f
661 # cat f
662 revision 1
662 revision 1
663 space
663 space
664 # hg stat
664 # hg stat
665 M f
665 M f
666 # hg resolve --list
666 # hg resolve --list
667 U f
667 U f
668
668
669 ui.merge specifies :local (without internal prefix):
669 ui.merge specifies :local (without internal prefix):
670
670
671 $ beforemerge
671 $ beforemerge
672 [merge-tools]
672 [merge-tools]
673 false.whatever=
673 false.whatever=
674 true.priority=1
674 true.priority=1
675 true.executable=cat
675 true.executable=cat
676 # hg update -C 1
676 # hg update -C 1
677 $ hg merge -r 2 --config ui.merge=:local
677 $ hg merge -r 2 --config ui.merge=:local
678 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
678 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
679 (branch merge, don't forget to commit)
679 (branch merge, don't forget to commit)
680 $ aftermerge
680 $ aftermerge
681 # cat f
681 # cat f
682 revision 1
682 revision 1
683 space
683 space
684 # hg stat
684 # hg stat
685 M f
685 M f
686 # hg resolve --list
686 # hg resolve --list
687 R f
687 R f
688
688
689 ui.merge specifies internal:other:
689 ui.merge specifies internal:other:
690
690
691 $ beforemerge
691 $ beforemerge
692 [merge-tools]
692 [merge-tools]
693 false.whatever=
693 false.whatever=
694 true.priority=1
694 true.priority=1
695 true.executable=cat
695 true.executable=cat
696 # hg update -C 1
696 # hg update -C 1
697 $ hg merge -r 2 --config ui.merge=internal:other
697 $ hg merge -r 2 --config ui.merge=internal:other
698 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
698 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
699 (branch merge, don't forget to commit)
699 (branch merge, don't forget to commit)
700 $ aftermerge
700 $ aftermerge
701 # cat f
701 # cat f
702 revision 2
702 revision 2
703 space
703 space
704 # hg stat
704 # hg stat
705 M f
705 M f
706 # hg resolve --list
706 # hg resolve --list
707 R f
707 R f
708
708
709 ui.merge specifies internal:prompt:
709 ui.merge specifies internal:prompt:
710
710
711 $ beforemerge
711 $ beforemerge
712 [merge-tools]
712 [merge-tools]
713 false.whatever=
713 false.whatever=
714 true.priority=1
714 true.priority=1
715 true.executable=cat
715 true.executable=cat
716 # hg update -C 1
716 # hg update -C 1
717 $ hg merge -r 2 --config ui.merge=internal:prompt
717 $ hg merge -r 2 --config ui.merge=internal:prompt
718 file 'f' needs to be resolved.
718 file 'f' needs to be resolved.
719 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
719 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
720 What do you want to do? u
720 What do you want to do? u
721 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
721 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
722 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
722 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
723 [1]
723 [1]
724 $ aftermerge
724 $ aftermerge
725 # cat f
725 # cat f
726 revision 1
726 revision 1
727 space
727 space
728 # hg stat
728 # hg stat
729 M f
729 M f
730 # hg resolve --list
730 # hg resolve --list
731 U f
731 U f
732
732
733 ui.merge specifies :prompt, with 'leave unresolved' chosen
733 ui.merge specifies :prompt, with 'leave unresolved' chosen
734
734
735 $ beforemerge
735 $ beforemerge
736 [merge-tools]
736 [merge-tools]
737 false.whatever=
737 false.whatever=
738 true.priority=1
738 true.priority=1
739 true.executable=cat
739 true.executable=cat
740 # hg update -C 1
740 # hg update -C 1
741 $ hg merge -r 2 --config ui.merge=:prompt --config ui.interactive=True << EOF
741 $ hg merge -r 2 --config ui.merge=:prompt --config ui.interactive=True << EOF
742 > u
742 > u
743 > EOF
743 > EOF
744 file 'f' needs to be resolved.
744 file 'f' needs to be resolved.
745 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
745 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
746 What do you want to do? u
746 What do you want to do? u
747 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
747 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
748 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
748 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
749 [1]
749 [1]
750 $ aftermerge
750 $ aftermerge
751 # cat f
751 # cat f
752 revision 1
752 revision 1
753 space
753 space
754 # hg stat
754 # hg stat
755 M f
755 M f
756 # hg resolve --list
756 # hg resolve --list
757 U f
757 U f
758
758
759 prompt with EOF
759 prompt with EOF
760
760
761 $ beforemerge
761 $ beforemerge
762 [merge-tools]
762 [merge-tools]
763 false.whatever=
763 false.whatever=
764 true.priority=1
764 true.priority=1
765 true.executable=cat
765 true.executable=cat
766 # hg update -C 1
766 # hg update -C 1
767 $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true
767 $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true
768 file 'f' needs to be resolved.
768 file 'f' needs to be resolved.
769 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
769 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
770 What do you want to do?
770 What do you want to do?
771 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
771 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
772 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
772 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
773 [1]
773 [1]
774 $ aftermerge
774 $ aftermerge
775 # cat f
775 # cat f
776 revision 1
776 revision 1
777 space
777 space
778 # hg stat
778 # hg stat
779 M f
779 M f
780 # hg resolve --list
780 # hg resolve --list
781 U f
781 U f
782 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
782 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
783 file 'f' needs to be resolved.
783 file 'f' needs to be resolved.
784 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
784 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
785 What do you want to do?
785 What do you want to do?
786 [1]
786 [1]
787 $ aftermerge
787 $ aftermerge
788 # cat f
788 # cat f
789 revision 1
789 revision 1
790 space
790 space
791 # hg stat
791 # hg stat
792 M f
792 M f
793 ? f.orig
793 ? f.orig
794 # hg resolve --list
794 # hg resolve --list
795 U f
795 U f
796 $ rm f
796 $ rm f
797 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
797 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
798 file 'f' needs to be resolved.
798 file 'f' needs to be resolved.
799 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
799 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
800 What do you want to do?
800 What do you want to do?
801 [1]
801 [1]
802 $ aftermerge
802 $ aftermerge
803 # cat f
803 # cat f
804 revision 1
804 revision 1
805 space
805 space
806 # hg stat
806 # hg stat
807 M f
807 M f
808 # hg resolve --list
808 # hg resolve --list
809 U f
809 U f
810 $ hg resolve --all --config ui.merge=internal:prompt
810 $ hg resolve --all --config ui.merge=internal:prompt
811 file 'f' needs to be resolved.
811 file 'f' needs to be resolved.
812 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
812 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
813 What do you want to do? u
813 What do you want to do? u
814 [1]
814 [1]
815 $ aftermerge
815 $ aftermerge
816 # cat f
816 # cat f
817 revision 1
817 revision 1
818 space
818 space
819 # hg stat
819 # hg stat
820 M f
820 M f
821 ? f.orig
821 ? f.orig
822 # hg resolve --list
822 # hg resolve --list
823 U f
823 U f
824
824
825 ui.merge specifies internal:dump:
825 ui.merge specifies internal:dump:
826
826
827 $ beforemerge
827 $ beforemerge
828 [merge-tools]
828 [merge-tools]
829 false.whatever=
829 false.whatever=
830 true.priority=1
830 true.priority=1
831 true.executable=cat
831 true.executable=cat
832 # hg update -C 1
832 # hg update -C 1
833 $ hg merge -r 2 --config ui.merge=internal:dump
833 $ hg merge -r 2 --config ui.merge=internal:dump
834 merging f
834 merging f
835 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
835 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
836 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
836 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
837 [1]
837 [1]
838 $ aftermerge
838 $ aftermerge
839 # cat f
839 # cat f
840 revision 1
840 revision 1
841 space
841 space
842 # hg stat
842 # hg stat
843 M f
843 M f
844 ? f.base
844 ? f.base
845 ? f.local
845 ? f.local
846 ? f.orig
846 ? f.orig
847 ? f.other
847 ? f.other
848 # hg resolve --list
848 # hg resolve --list
849 U f
849 U f
850
850
851 f.base:
851 f.base:
852
852
853 $ cat f.base
853 $ cat f.base
854 revision 0
854 revision 0
855 space
855 space
856
856
857 f.local:
857 f.local:
858
858
859 $ cat f.local
859 $ cat f.local
860 revision 1
860 revision 1
861 space
861 space
862
862
863 f.other:
863 f.other:
864
864
865 $ cat f.other
865 $ cat f.other
866 revision 2
866 revision 2
867 space
867 space
868 $ rm f.base f.local f.other
868 $ rm f.base f.local f.other
869
869
870 check that internal:dump doesn't dump files if premerge runs
870 check that internal:dump doesn't dump files if premerge runs
871 successfully
871 successfully
872
872
873 $ beforemerge
873 $ beforemerge
874 [merge-tools]
874 [merge-tools]
875 false.whatever=
875 false.whatever=
876 true.priority=1
876 true.priority=1
877 true.executable=cat
877 true.executable=cat
878 # hg update -C 1
878 # hg update -C 1
879 $ hg merge -r 3 --config ui.merge=internal:dump
879 $ hg merge -r 3 --config ui.merge=internal:dump
880 merging f
880 merging f
881 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
881 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
882 (branch merge, don't forget to commit)
882 (branch merge, don't forget to commit)
883
883
884 $ aftermerge
884 $ aftermerge
885 # cat f
885 # cat f
886 revision 1
886 revision 1
887 space
887 space
888 revision 3
888 revision 3
889 # hg stat
889 # hg stat
890 M f
890 M f
891 # hg resolve --list
891 # hg resolve --list
892 R f
892 R f
893
893
894 check that internal:forcedump dumps files, even if local and other can
894 check that internal:forcedump dumps files, even if local and other can
895 be merged easily
895 be merged easily
896
896
897 $ beforemerge
897 $ beforemerge
898 [merge-tools]
898 [merge-tools]
899 false.whatever=
899 false.whatever=
900 true.priority=1
900 true.priority=1
901 true.executable=cat
901 true.executable=cat
902 # hg update -C 1
902 # hg update -C 1
903 $ hg merge -r 3 --config ui.merge=internal:forcedump
903 $ hg merge -r 3 --config ui.merge=internal:forcedump
904 merging f
904 merging f
905 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
905 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
906 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
906 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
907 [1]
907 [1]
908 $ aftermerge
908 $ aftermerge
909 # cat f
909 # cat f
910 revision 1
910 revision 1
911 space
911 space
912 # hg stat
912 # hg stat
913 M f
913 M f
914 ? f.base
914 ? f.base
915 ? f.local
915 ? f.local
916 ? f.orig
916 ? f.orig
917 ? f.other
917 ? f.other
918 # hg resolve --list
918 # hg resolve --list
919 U f
919 U f
920
920
921 $ cat f.base
921 $ cat f.base
922 revision 0
922 revision 0
923 space
923 space
924
924
925 $ cat f.local
925 $ cat f.local
926 revision 1
926 revision 1
927 space
927 space
928
928
929 $ cat f.other
929 $ cat f.other
930 revision 0
930 revision 0
931 space
931 space
932 revision 3
932 revision 3
933
933
934 $ rm -f f.base f.local f.other
934 $ rm -f f.base f.local f.other
935
935
936 ui.merge specifies internal:other but is overruled by pattern for false:
936 ui.merge specifies internal:other but is overruled by pattern for false:
937
937
938 $ beforemerge
938 $ beforemerge
939 [merge-tools]
939 [merge-tools]
940 false.whatever=
940 false.whatever=
941 true.priority=1
941 true.priority=1
942 true.executable=cat
942 true.executable=cat
943 # hg update -C 1
943 # hg update -C 1
944 $ hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
944 $ hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
945 merging f
945 merging f
946 merging f failed!
946 merging f failed!
947 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
947 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
948 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
948 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
949 [1]
949 [1]
950 $ aftermerge
950 $ aftermerge
951 # cat f
951 # cat f
952 revision 1
952 revision 1
953 space
953 space
954 # hg stat
954 # hg stat
955 M f
955 M f
956 ? f.orig
956 ? f.orig
957 # hg resolve --list
957 # hg resolve --list
958 U f
958 U f
959
959
960 Premerge
960 Premerge
961
961
962 ui.merge specifies internal:other but is overruled by --tool=false
962 ui.merge specifies internal:other but is overruled by --tool=false
963
963
964 $ beforemerge
964 $ beforemerge
965 [merge-tools]
965 [merge-tools]
966 false.whatever=
966 false.whatever=
967 true.priority=1
967 true.priority=1
968 true.executable=cat
968 true.executable=cat
969 # hg update -C 1
969 # hg update -C 1
970 $ hg merge -r 2 --config ui.merge=internal:other --tool=false
970 $ hg merge -r 2 --config ui.merge=internal:other --tool=false
971 merging f
971 merging f
972 merging f failed!
972 merging f failed!
973 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
973 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
974 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
974 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
975 [1]
975 [1]
976 $ aftermerge
976 $ aftermerge
977 # cat f
977 # cat f
978 revision 1
978 revision 1
979 space
979 space
980 # hg stat
980 # hg stat
981 M f
981 M f
982 ? f.orig
982 ? f.orig
983 # hg resolve --list
983 # hg resolve --list
984 U f
984 U f
985
985
986 HGMERGE specifies internal:other but is overruled by --tool=false
986 HGMERGE specifies internal:other but is overruled by --tool=false
987
987
988 $ HGMERGE=internal:other ; export HGMERGE
988 $ HGMERGE=internal:other ; export HGMERGE
989 $ beforemerge
989 $ beforemerge
990 [merge-tools]
990 [merge-tools]
991 false.whatever=
991 false.whatever=
992 true.priority=1
992 true.priority=1
993 true.executable=cat
993 true.executable=cat
994 # hg update -C 1
994 # hg update -C 1
995 $ hg merge -r 2 --tool=false
995 $ hg merge -r 2 --tool=false
996 merging f
996 merging f
997 merging f failed!
997 merging f failed!
998 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
998 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
999 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
999 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1000 [1]
1000 [1]
1001 $ aftermerge
1001 $ aftermerge
1002 # cat f
1002 # cat f
1003 revision 1
1003 revision 1
1004 space
1004 space
1005 # hg stat
1005 # hg stat
1006 M f
1006 M f
1007 ? f.orig
1007 ? f.orig
1008 # hg resolve --list
1008 # hg resolve --list
1009 U f
1009 U f
1010
1010
1011 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
1011 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
1012
1012
1013 update is a merge ...
1013 update is a merge ...
1014
1014
1015 (this also tests that files reverted with '--rev REV' are treated as
1015 (this also tests that files reverted with '--rev REV' are treated as
1016 "modified", even if none of mode, size and timestamp of them isn't
1016 "modified", even if none of mode, size and timestamp of them isn't
1017 changed on the filesystem (see also issue4583))
1017 changed on the filesystem (see also issue4583))
1018
1018
1019 $ cat >> $HGRCPATH <<EOF
1019 $ cat >> $HGRCPATH <<EOF
1020 > [fakedirstatewritetime]
1020 > [fakedirstatewritetime]
1021 > # emulate invoking dirstate.write() via repo.status()
1021 > # emulate invoking dirstate.write() via repo.status()
1022 > # at 2000-01-01 00:00
1022 > # at 2000-01-01 00:00
1023 > fakenow = 200001010000
1023 > fakenow = 200001010000
1024 > EOF
1024 > EOF
1025
1025
1026 $ beforemerge
1026 $ beforemerge
1027 [merge-tools]
1027 [merge-tools]
1028 false.whatever=
1028 false.whatever=
1029 true.priority=1
1029 true.priority=1
1030 true.executable=cat
1030 true.executable=cat
1031 # hg update -C 1
1031 # hg update -C 1
1032 $ hg update -q 0
1032 $ hg update -q 0
1033 $ f -s f
1033 $ f -s f
1034 f: size=17
1034 f: size=17
1035 $ touch -t 200001010000 f
1035 $ touch -t 200001010000 f
1036 $ hg debugrebuildstate
1036 $ hg debugrebuildstate
1037 $ cat >> $HGRCPATH <<EOF
1037 $ cat >> $HGRCPATH <<EOF
1038 > [extensions]
1038 > [extensions]
1039 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1039 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1040 > EOF
1040 > EOF
1041 $ hg revert -q -r 1 .
1041 $ hg revert -q -r 1 .
1042 $ cat >> $HGRCPATH <<EOF
1042 $ cat >> $HGRCPATH <<EOF
1043 > [extensions]
1043 > [extensions]
1044 > fakedirstatewritetime = !
1044 > fakedirstatewritetime = !
1045 > EOF
1045 > EOF
1046 $ f -s f
1046 $ f -s f
1047 f: size=17
1047 f: size=17
1048 $ touch -t 200001010000 f
1048 $ touch -t 200001010000 f
1049 $ hg status f
1049 $ hg status f
1050 M f
1050 M f
1051 $ hg update -r 2
1051 $ hg update -r 2
1052 merging f
1052 merging f
1053 revision 1
1053 revision 1
1054 space
1054 space
1055 revision 0
1055 revision 0
1056 space
1056 space
1057 revision 2
1057 revision 2
1058 space
1058 space
1059 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1059 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1060 $ aftermerge
1060 $ aftermerge
1061 # cat f
1061 # cat f
1062 revision 1
1062 revision 1
1063 space
1063 space
1064 # hg stat
1064 # hg stat
1065 M f
1065 M f
1066 # hg resolve --list
1066 # hg resolve --list
1067 R f
1067 R f
1068
1068
1069 update should also have --tool
1069 update should also have --tool
1070
1070
1071 $ beforemerge
1071 $ beforemerge
1072 [merge-tools]
1072 [merge-tools]
1073 false.whatever=
1073 false.whatever=
1074 true.priority=1
1074 true.priority=1
1075 true.executable=cat
1075 true.executable=cat
1076 # hg update -C 1
1076 # hg update -C 1
1077 $ hg update -q 0
1077 $ hg update -q 0
1078 $ f -s f
1078 $ f -s f
1079 f: size=17
1079 f: size=17
1080 $ touch -t 200001010000 f
1080 $ touch -t 200001010000 f
1081 $ hg debugrebuildstate
1081 $ hg debugrebuildstate
1082 $ cat >> $HGRCPATH <<EOF
1082 $ cat >> $HGRCPATH <<EOF
1083 > [extensions]
1083 > [extensions]
1084 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1084 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1085 > EOF
1085 > EOF
1086 $ hg revert -q -r 1 .
1086 $ hg revert -q -r 1 .
1087 $ cat >> $HGRCPATH <<EOF
1087 $ cat >> $HGRCPATH <<EOF
1088 > [extensions]
1088 > [extensions]
1089 > fakedirstatewritetime = !
1089 > fakedirstatewritetime = !
1090 > EOF
1090 > EOF
1091 $ f -s f
1091 $ f -s f
1092 f: size=17
1092 f: size=17
1093 $ touch -t 200001010000 f
1093 $ touch -t 200001010000 f
1094 $ hg status f
1094 $ hg status f
1095 M f
1095 M f
1096 $ hg update -r 2 --tool false
1096 $ hg update -r 2 --tool false
1097 merging f
1097 merging f
1098 merging f failed!
1098 merging f failed!
1099 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1099 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1100 use 'hg resolve' to retry unresolved file merges
1100 use 'hg resolve' to retry unresolved file merges
1101 [1]
1101 [1]
1102 $ aftermerge
1102 $ aftermerge
1103 # cat f
1103 # cat f
1104 revision 1
1104 revision 1
1105 space
1105 space
1106 # hg stat
1106 # hg stat
1107 M f
1107 M f
1108 ? f.orig
1108 ? f.orig
1109 # hg resolve --list
1109 # hg resolve --list
1110 U f
1110 U f
1111
1111
1112 Default is silent simplemerge:
1112 Default is silent simplemerge:
1113
1113
1114 $ beforemerge
1114 $ beforemerge
1115 [merge-tools]
1115 [merge-tools]
1116 false.whatever=
1116 false.whatever=
1117 true.priority=1
1117 true.priority=1
1118 true.executable=cat
1118 true.executable=cat
1119 # hg update -C 1
1119 # hg update -C 1
1120 $ hg merge -r 3
1120 $ hg merge -r 3
1121 merging f
1121 merging f
1122 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1122 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1123 (branch merge, don't forget to commit)
1123 (branch merge, don't forget to commit)
1124 $ aftermerge
1124 $ aftermerge
1125 # cat f
1125 # cat f
1126 revision 1
1126 revision 1
1127 space
1127 space
1128 revision 3
1128 revision 3
1129 # hg stat
1129 # hg stat
1130 M f
1130 M f
1131 # hg resolve --list
1131 # hg resolve --list
1132 R f
1132 R f
1133
1133
1134 .premerge=True is same:
1134 .premerge=True is same:
1135
1135
1136 $ beforemerge
1136 $ beforemerge
1137 [merge-tools]
1137 [merge-tools]
1138 false.whatever=
1138 false.whatever=
1139 true.priority=1
1139 true.priority=1
1140 true.executable=cat
1140 true.executable=cat
1141 # hg update -C 1
1141 # hg update -C 1
1142 $ hg merge -r 3 --config merge-tools.true.premerge=True
1142 $ hg merge -r 3 --config merge-tools.true.premerge=True
1143 merging f
1143 merging f
1144 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1144 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1145 (branch merge, don't forget to commit)
1145 (branch merge, don't forget to commit)
1146 $ aftermerge
1146 $ aftermerge
1147 # cat f
1147 # cat f
1148 revision 1
1148 revision 1
1149 space
1149 space
1150 revision 3
1150 revision 3
1151 # hg stat
1151 # hg stat
1152 M f
1152 M f
1153 # hg resolve --list
1153 # hg resolve --list
1154 R f
1154 R f
1155
1155
1156 .premerge=False executes merge-tool:
1156 .premerge=False executes merge-tool:
1157
1157
1158 $ beforemerge
1158 $ beforemerge
1159 [merge-tools]
1159 [merge-tools]
1160 false.whatever=
1160 false.whatever=
1161 true.priority=1
1161 true.priority=1
1162 true.executable=cat
1162 true.executable=cat
1163 # hg update -C 1
1163 # hg update -C 1
1164 $ hg merge -r 3 --config merge-tools.true.premerge=False
1164 $ hg merge -r 3 --config merge-tools.true.premerge=False
1165 merging f
1165 merging f
1166 revision 1
1166 revision 1
1167 space
1167 space
1168 revision 0
1168 revision 0
1169 space
1169 space
1170 revision 0
1170 revision 0
1171 space
1171 space
1172 revision 3
1172 revision 3
1173 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1173 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1174 (branch merge, don't forget to commit)
1174 (branch merge, don't forget to commit)
1175 $ aftermerge
1175 $ aftermerge
1176 # cat f
1176 # cat f
1177 revision 1
1177 revision 1
1178 space
1178 space
1179 # hg stat
1179 # hg stat
1180 M f
1180 M f
1181 # hg resolve --list
1181 # hg resolve --list
1182 R f
1182 R f
1183
1183
1184 premerge=keep keeps conflict markers in:
1184 premerge=keep keeps conflict markers in:
1185
1185
1186 $ beforemerge
1186 $ beforemerge
1187 [merge-tools]
1187 [merge-tools]
1188 false.whatever=
1188 false.whatever=
1189 true.priority=1
1189 true.priority=1
1190 true.executable=cat
1190 true.executable=cat
1191 # hg update -C 1
1191 # hg update -C 1
1192 $ hg merge -r 4 --config merge-tools.true.premerge=keep
1192 $ hg merge -r 4 --config merge-tools.true.premerge=keep
1193 merging f
1193 merging f
1194 <<<<<<< working copy: ef83787e2614 - test: revision 1
1194 <<<<<<< working copy: ef83787e2614 - test: revision 1
1195 revision 1
1195 revision 1
1196 space
1196 space
1197 =======
1197 =======
1198 revision 4
1198 revision 4
1199 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1199 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1200 revision 0
1200 revision 0
1201 space
1201 space
1202 revision 4
1202 revision 4
1203 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1203 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1204 (branch merge, don't forget to commit)
1204 (branch merge, don't forget to commit)
1205 $ aftermerge
1205 $ aftermerge
1206 # cat f
1206 # cat f
1207 <<<<<<< working copy: ef83787e2614 - test: revision 1
1207 <<<<<<< working copy: ef83787e2614 - test: revision 1
1208 revision 1
1208 revision 1
1209 space
1209 space
1210 =======
1210 =======
1211 revision 4
1211 revision 4
1212 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1212 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1213 # hg stat
1213 # hg stat
1214 M f
1214 M f
1215 # hg resolve --list
1215 # hg resolve --list
1216 R f
1216 R f
1217
1217
1218 premerge=keep-merge3 keeps conflict markers with base content:
1218 premerge=keep-merge3 keeps conflict markers with base content:
1219
1219
1220 $ beforemerge
1220 $ beforemerge
1221 [merge-tools]
1221 [merge-tools]
1222 false.whatever=
1222 false.whatever=
1223 true.priority=1
1223 true.priority=1
1224 true.executable=cat
1224 true.executable=cat
1225 # hg update -C 1
1225 # hg update -C 1
1226 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
1226 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
1227 merging f
1227 merging f
1228 <<<<<<< working copy: ef83787e2614 - test: revision 1
1228 <<<<<<< working copy: ef83787e2614 - test: revision 1
1229 revision 1
1229 revision 1
1230 space
1230 space
1231 ||||||| base
1231 ||||||| base
1232 revision 0
1232 revision 0
1233 space
1233 space
1234 =======
1234 =======
1235 revision 4
1235 revision 4
1236 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1236 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1237 revision 0
1237 revision 0
1238 space
1238 space
1239 revision 4
1239 revision 4
1240 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1240 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1241 (branch merge, don't forget to commit)
1241 (branch merge, don't forget to commit)
1242 $ aftermerge
1242 $ aftermerge
1243 # cat f
1243 # cat f
1244 <<<<<<< working copy: ef83787e2614 - test: revision 1
1244 <<<<<<< working copy: ef83787e2614 - test: revision 1
1245 revision 1
1245 revision 1
1246 space
1246 space
1247 ||||||| base
1247 ||||||| base
1248 revision 0
1248 revision 0
1249 space
1249 space
1250 =======
1250 =======
1251 revision 4
1251 revision 4
1252 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1252 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1253 # hg stat
1253 # hg stat
1254 M f
1254 M f
1255 # hg resolve --list
1255 # hg resolve --list
1256 R f
1256 R f
1257
1257
1258 premerge=keep respects ui.mergemarkers=basic:
1258 premerge=keep respects ui.mergemarkers=basic:
1259
1259
1260 $ beforemerge
1260 $ beforemerge
1261 [merge-tools]
1261 [merge-tools]
1262 false.whatever=
1262 false.whatever=
1263 true.priority=1
1263 true.priority=1
1264 true.executable=cat
1264 true.executable=cat
1265 # hg update -C 1
1265 # hg update -C 1
1266 $ hg merge -r 4 --config merge-tools.true.premerge=keep --config ui.mergemarkers=basic
1266 $ hg merge -r 4 --config merge-tools.true.premerge=keep --config ui.mergemarkers=basic
1267 merging f
1267 merging f
1268 <<<<<<< working copy
1268 <<<<<<< working copy
1269 revision 1
1269 revision 1
1270 space
1270 space
1271 =======
1271 =======
1272 revision 4
1272 revision 4
1273 >>>>>>> merge rev
1273 >>>>>>> merge rev
1274 revision 0
1274 revision 0
1275 space
1275 space
1276 revision 4
1276 revision 4
1277 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1277 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1278 (branch merge, don't forget to commit)
1278 (branch merge, don't forget to commit)
1279 $ aftermerge
1279 $ aftermerge
1280 # cat f
1280 # cat f
1281 <<<<<<< working copy
1281 <<<<<<< working copy
1282 revision 1
1282 revision 1
1283 space
1283 space
1284 =======
1284 =======
1285 revision 4
1285 revision 4
1286 >>>>>>> merge rev
1286 >>>>>>> merge rev
1287 # hg stat
1287 # hg stat
1288 M f
1288 M f
1289 # hg resolve --list
1289 # hg resolve --list
1290 R f
1290 R f
1291
1291
1292 premerge=keep ignores ui.mergemarkers=basic if true.mergemarkers=detailed:
1292 premerge=keep ignores ui.mergemarkers=basic if true.mergemarkers=detailed:
1293
1293
1294 $ beforemerge
1294 $ beforemerge
1295 [merge-tools]
1295 [merge-tools]
1296 false.whatever=
1296 false.whatever=
1297 true.priority=1
1297 true.priority=1
1298 true.executable=cat
1298 true.executable=cat
1299 # hg update -C 1
1299 # hg update -C 1
1300 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1300 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1301 > --config ui.mergemarkers=basic \
1301 > --config ui.mergemarkers=basic \
1302 > --config merge-tools.true.mergemarkers=detailed
1302 > --config merge-tools.true.mergemarkers=detailed
1303 merging f
1303 merging f
1304 <<<<<<< working copy: ef83787e2614 - test: revision 1
1304 <<<<<<< working copy: ef83787e2614 - test: revision 1
1305 revision 1
1305 revision 1
1306 space
1306 space
1307 =======
1307 =======
1308 revision 4
1308 revision 4
1309 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1309 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1310 revision 0
1310 revision 0
1311 space
1311 space
1312 revision 4
1312 revision 4
1313 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1313 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1314 (branch merge, don't forget to commit)
1314 (branch merge, don't forget to commit)
1315 $ aftermerge
1315 $ aftermerge
1316 # cat f
1316 # cat f
1317 <<<<<<< working copy: ef83787e2614 - test: revision 1
1317 <<<<<<< working copy: ef83787e2614 - test: revision 1
1318 revision 1
1318 revision 1
1319 space
1319 space
1320 =======
1320 =======
1321 revision 4
1321 revision 4
1322 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1322 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1323 # hg stat
1323 # hg stat
1324 M f
1324 M f
1325 # hg resolve --list
1325 # hg resolve --list
1326 R f
1326 R f
1327
1327
1328 premerge=keep respects ui.mergemarkertemplate instead of
1328 premerge=keep respects ui.mergemarkertemplate instead of
1329 true.mergemarkertemplate if true.mergemarkers=basic:
1329 true.mergemarkertemplate if true.mergemarkers=basic:
1330
1330
1331 $ beforemerge
1331 $ beforemerge
1332 [merge-tools]
1332 [merge-tools]
1333 false.whatever=
1333 false.whatever=
1334 true.priority=1
1334 true.priority=1
1335 true.executable=cat
1335 true.executable=cat
1336 # hg update -C 1
1336 # hg update -C 1
1337 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1337 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1338 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1338 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1339 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}'
1339 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}'
1340 merging f
1340 merging f
1341 <<<<<<< working copy: uitmpl 1
1341 <<<<<<< working copy: uitmpl 1
1342 revision 1
1342 revision 1
1343 space
1343 space
1344 =======
1344 =======
1345 revision 4
1345 revision 4
1346 >>>>>>> merge rev: uitmpl 4
1346 >>>>>>> merge rev: uitmpl 4
1347 revision 0
1347 revision 0
1348 space
1348 space
1349 revision 4
1349 revision 4
1350 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1350 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1351 (branch merge, don't forget to commit)
1351 (branch merge, don't forget to commit)
1352 $ aftermerge
1352 $ aftermerge
1353 # cat f
1353 # cat f
1354 <<<<<<< working copy: uitmpl 1
1354 <<<<<<< working copy: uitmpl 1
1355 revision 1
1355 revision 1
1356 space
1356 space
1357 =======
1357 =======
1358 revision 4
1358 revision 4
1359 >>>>>>> merge rev: uitmpl 4
1359 >>>>>>> merge rev: uitmpl 4
1360 # hg stat
1360 # hg stat
1361 M f
1361 M f
1362 # hg resolve --list
1362 # hg resolve --list
1363 R f
1363 R f
1364
1364
1365 premerge=keep respects true.mergemarkertemplate instead of
1365 premerge=keep respects true.mergemarkertemplate instead of
1366 true.mergemarkertemplate if true.mergemarkers=detailed:
1366 true.mergemarkertemplate if true.mergemarkers=detailed:
1367
1367
1368 $ beforemerge
1368 $ beforemerge
1369 [merge-tools]
1369 [merge-tools]
1370 false.whatever=
1370 false.whatever=
1371 true.priority=1
1371 true.priority=1
1372 true.executable=cat
1372 true.executable=cat
1373 # hg update -C 1
1373 # hg update -C 1
1374 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1374 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1375 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1375 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1376 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1376 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1377 > --config merge-tools.true.mergemarkers=detailed
1377 > --config merge-tools.true.mergemarkers=detailed
1378 merging f
1378 merging f
1379 <<<<<<< working copy: tooltmpl ef83787e2614
1379 <<<<<<< working copy: tooltmpl ef83787e2614
1380 revision 1
1380 revision 1
1381 space
1381 space
1382 =======
1382 =======
1383 revision 4
1383 revision 4
1384 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1384 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1385 revision 0
1385 revision 0
1386 space
1386 space
1387 revision 4
1387 revision 4
1388 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1388 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1389 (branch merge, don't forget to commit)
1389 (branch merge, don't forget to commit)
1390 $ aftermerge
1390 $ aftermerge
1391 # cat f
1391 # cat f
1392 <<<<<<< working copy: tooltmpl ef83787e2614
1392 <<<<<<< working copy: tooltmpl ef83787e2614
1393 revision 1
1393 revision 1
1394 space
1394 space
1395 =======
1395 =======
1396 revision 4
1396 revision 4
1397 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1397 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1398 # hg stat
1398 # hg stat
1399 M f
1399 M f
1400 # hg resolve --list
1400 # hg resolve --list
1401 R f
1401 R f
1402
1402
1403 Tool execution
1403 Tool execution
1404
1404
1405 set tools.args explicit to include $base $local $other $output:
1405 set tools.args explicit to include $base $local $other $output:
1406
1406
1407 $ beforemerge
1407 $ beforemerge
1408 [merge-tools]
1408 [merge-tools]
1409 false.whatever=
1409 false.whatever=
1410 true.priority=1
1410 true.priority=1
1411 true.executable=cat
1411 true.executable=cat
1412 # hg update -C 1
1412 # hg update -C 1
1413 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
1413 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
1414 > | sed 's,==> .* <==,==> ... <==,g'
1414 > | sed 's,==> .* <==,==> ... <==,g'
1415 merging f
1415 merging f
1416 ==> ... <==
1416 ==> ... <==
1417 revision 0
1417 revision 0
1418 space
1418 space
1419
1419
1420 ==> ... <==
1420 ==> ... <==
1421 revision 1
1421 revision 1
1422 space
1422 space
1423
1423
1424 ==> ... <==
1424 ==> ... <==
1425 revision 2
1425 revision 2
1426 space
1426 space
1427
1427
1428 ==> ... <==
1428 ==> ... <==
1429 revision 1
1429 revision 1
1430 space
1430 space
1431 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1431 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1432 (branch merge, don't forget to commit)
1432 (branch merge, don't forget to commit)
1433 $ aftermerge
1433 $ aftermerge
1434 # cat f
1434 # cat f
1435 revision 1
1435 revision 1
1436 space
1436 space
1437 # hg stat
1437 # hg stat
1438 M f
1438 M f
1439 # hg resolve --list
1439 # hg resolve --list
1440 R f
1440 R f
1441
1441
1442 Merge with "echo mergeresult > $local":
1442 Merge with "echo mergeresult > $local":
1443
1443
1444 $ beforemerge
1444 $ beforemerge
1445 [merge-tools]
1445 [merge-tools]
1446 false.whatever=
1446 false.whatever=
1447 true.priority=1
1447 true.priority=1
1448 true.executable=cat
1448 true.executable=cat
1449 # hg update -C 1
1449 # hg update -C 1
1450 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
1450 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
1451 merging f
1451 merging f
1452 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1452 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1453 (branch merge, don't forget to commit)
1453 (branch merge, don't forget to commit)
1454 $ aftermerge
1454 $ aftermerge
1455 # cat f
1455 # cat f
1456 mergeresult
1456 mergeresult
1457 # hg stat
1457 # hg stat
1458 M f
1458 M f
1459 # hg resolve --list
1459 # hg resolve --list
1460 R f
1460 R f
1461
1461
1462 - and $local is the file f:
1462 - and $local is the file f:
1463
1463
1464 $ beforemerge
1464 $ beforemerge
1465 [merge-tools]
1465 [merge-tools]
1466 false.whatever=
1466 false.whatever=
1467 true.priority=1
1467 true.priority=1
1468 true.executable=cat
1468 true.executable=cat
1469 # hg update -C 1
1469 # hg update -C 1
1470 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
1470 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
1471 merging f
1471 merging f
1472 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1472 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1473 (branch merge, don't forget to commit)
1473 (branch merge, don't forget to commit)
1474 $ aftermerge
1474 $ aftermerge
1475 # cat f
1475 # cat f
1476 mergeresult
1476 mergeresult
1477 # hg stat
1477 # hg stat
1478 M f
1478 M f
1479 # hg resolve --list
1479 # hg resolve --list
1480 R f
1480 R f
1481
1481
1482 Merge with "echo mergeresult > $output" - the variable is a bit magic:
1482 Merge with "echo mergeresult > $output" - the variable is a bit magic:
1483
1483
1484 $ beforemerge
1484 $ beforemerge
1485 [merge-tools]
1485 [merge-tools]
1486 false.whatever=
1486 false.whatever=
1487 true.priority=1
1487 true.priority=1
1488 true.executable=cat
1488 true.executable=cat
1489 # hg update -C 1
1489 # hg update -C 1
1490 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
1490 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
1491 merging f
1491 merging f
1492 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1492 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1493 (branch merge, don't forget to commit)
1493 (branch merge, don't forget to commit)
1494 $ aftermerge
1494 $ aftermerge
1495 # cat f
1495 # cat f
1496 mergeresult
1496 mergeresult
1497 # hg stat
1497 # hg stat
1498 M f
1498 M f
1499 # hg resolve --list
1499 # hg resolve --list
1500 R f
1500 R f
1501
1501
1502 Merge using tool with a path that must be quoted:
1502 Merge using tool with a path that must be quoted:
1503
1503
1504 $ beforemerge
1504 $ beforemerge
1505 [merge-tools]
1505 [merge-tools]
1506 false.whatever=
1506 false.whatever=
1507 true.priority=1
1507 true.priority=1
1508 true.executable=cat
1508 true.executable=cat
1509 # hg update -C 1
1509 # hg update -C 1
1510 $ cat <<EOF > 'my merge tool'
1510 $ cat <<EOF > 'my merge tool'
1511 > cat "\$1" "\$2" "\$3" > "\$4"
1511 > cat "\$1" "\$2" "\$3" > "\$4"
1512 > EOF
1512 > EOF
1513 $ hg --config merge-tools.true.executable='sh' \
1513 $ hg --config merge-tools.true.executable='sh' \
1514 > --config merge-tools.true.args='"./my merge tool" $base $local $other $output' \
1514 > --config merge-tools.true.args='"./my merge tool" $base $local $other $output' \
1515 > merge -r 2
1515 > merge -r 2
1516 merging f
1516 merging f
1517 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1517 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1518 (branch merge, don't forget to commit)
1518 (branch merge, don't forget to commit)
1519 $ rm -f 'my merge tool'
1519 $ rm -f 'my merge tool'
1520 $ aftermerge
1520 $ aftermerge
1521 # cat f
1521 # cat f
1522 revision 0
1522 revision 0
1523 space
1523 space
1524 revision 1
1524 revision 1
1525 space
1525 space
1526 revision 2
1526 revision 2
1527 space
1527 space
1528 # hg stat
1528 # hg stat
1529 M f
1529 M f
1530 # hg resolve --list
1530 # hg resolve --list
1531 R f
1531 R f
1532
1532
1533 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1533 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1534 that they're quoted properly as well. This is using the default 'basic'
1534 that they're quoted properly as well. This is using the default 'basic'
1535 mergemarkers even though ui.mergemarkers is 'detailed', so it's ignoring both
1535 mergemarkers even though ui.mergemarkers is 'detailed', so it's ignoring both
1536 mergemarkertemplate settings:
1536 mergemarkertemplate settings:
1537
1537
1538 $ beforemerge
1538 $ beforemerge
1539 [merge-tools]
1539 [merge-tools]
1540 false.whatever=
1540 false.whatever=
1541 true.priority=1
1541 true.priority=1
1542 true.executable=cat
1542 true.executable=cat
1543 # hg update -C 1
1543 # hg update -C 1
1544 $ cat <<EOF > printargs_merge_tool
1544 $ cat <<EOF > printargs_merge_tool
1545 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1545 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1546 > EOF
1546 > EOF
1547 $ hg --config merge-tools.true.executable='sh' \
1547 $ hg --config merge-tools.true.executable='sh' \
1548 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1548 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1549 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1549 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1550 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1550 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1551 > --config ui.mergemarkers=detailed \
1551 > --config ui.mergemarkers=detailed \
1552 > merge -r 2
1552 > merge -r 2
1553 merging f
1553 merging f
1554 arg: "ll:working copy"
1554 arg: "ll:working copy"
1555 arg: "lo:"
1555 arg: "lo:"
1556 arg: "merge rev"
1556 arg: "merge rev"
1557 arg: "lb:base: */f~base.*" (glob)
1557 arg: "lb:base: */f~base.*" (glob)
1558 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1558 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1559 (branch merge, don't forget to commit)
1559 (branch merge, don't forget to commit)
1560 $ rm -f 'printargs_merge_tool'
1560 $ rm -f 'printargs_merge_tool'
1561
1561
1562 Same test with experimental.mergetempdirprefix set:
1562 Same test with experimental.mergetempdirprefix set:
1563
1563
1564 $ beforemerge
1564 $ beforemerge
1565 [merge-tools]
1565 [merge-tools]
1566 false.whatever=
1566 false.whatever=
1567 true.priority=1
1567 true.priority=1
1568 true.executable=cat
1568 true.executable=cat
1569 # hg update -C 1
1569 # hg update -C 1
1570 $ cat <<EOF > printargs_merge_tool
1570 $ cat <<EOF > printargs_merge_tool
1571 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1571 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1572 > EOF
1572 > EOF
1573 $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
1573 $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
1574 > --config merge-tools.true.executable='sh' \
1574 > --config merge-tools.true.executable='sh' \
1575 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1575 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1576 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1576 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1577 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1577 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1578 > --config ui.mergemarkers=detailed \
1578 > --config ui.mergemarkers=detailed \
1579 > merge -r 2
1579 > merge -r 2
1580 merging f
1580 merging f
1581 arg: "ll:working copy"
1581 arg: "ll:working copy"
1582 arg: "lo:"
1582 arg: "lo:"
1583 arg: "merge rev"
1583 arg: "merge rev"
1584 arg: "lb:base: */hgmerge.*/f~base" (glob)
1584 arg: "lb:base: */hgmerge.*/f~base" (glob)
1585 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1585 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1586 (branch merge, don't forget to commit)
1586 (branch merge, don't forget to commit)
1587 $ rm -f 'printargs_merge_tool'
1587 $ rm -f 'printargs_merge_tool'
1588
1588
1589 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1589 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1590 that they're quoted properly as well. This is using 'detailed' mergemarkers,
1590 that they're quoted properly as well. This is using 'detailed' mergemarkers,
1591 even though ui.mergemarkers is 'basic', and using the tool's
1591 even though ui.mergemarkers is 'basic', and using the tool's
1592 mergemarkertemplate:
1592 mergemarkertemplate:
1593
1593
1594 $ beforemerge
1594 $ beforemerge
1595 [merge-tools]
1595 [merge-tools]
1596 false.whatever=
1596 false.whatever=
1597 true.priority=1
1597 true.priority=1
1598 true.executable=cat
1598 true.executable=cat
1599 # hg update -C 1
1599 # hg update -C 1
1600 $ cat <<EOF > printargs_merge_tool
1600 $ cat <<EOF > printargs_merge_tool
1601 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1601 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1602 > EOF
1602 > EOF
1603 $ hg --config merge-tools.true.executable='sh' \
1603 $ hg --config merge-tools.true.executable='sh' \
1604 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1604 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1605 > --config merge-tools.true.mergemarkers=detailed \
1605 > --config merge-tools.true.mergemarkers=detailed \
1606 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1606 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1607 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1607 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1608 > --config ui.mergemarkers=basic \
1608 > --config ui.mergemarkers=basic \
1609 > merge -r 2
1609 > merge -r 2
1610 merging f
1610 merging f
1611 arg: "ll:working copy: tooltmpl ef83787e2614"
1611 arg: "ll:working copy: tooltmpl ef83787e2614"
1612 arg: "lo:"
1612 arg: "lo:"
1613 arg: "merge rev: tooltmpl 0185f4e0cf02"
1613 arg: "merge rev: tooltmpl 0185f4e0cf02"
1614 arg: "lb:base: */f~base.*" (glob)
1614 arg: "lb:base: */f~base.*" (glob)
1615 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1615 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1616 (branch merge, don't forget to commit)
1616 (branch merge, don't forget to commit)
1617 $ rm -f 'printargs_merge_tool'
1617 $ rm -f 'printargs_merge_tool'
1618
1618
1619 The merge tool still gets labellocal and labelother as 'basic' even when
1619 The merge tool still gets labellocal and labelother as 'basic' even when
1620 premerge=keep is used and has 'detailed' markers:
1620 premerge=keep is used and has 'detailed' markers:
1621
1621
1622 $ beforemerge
1622 $ beforemerge
1623 [merge-tools]
1623 [merge-tools]
1624 false.whatever=
1624 false.whatever=
1625 true.priority=1
1625 true.priority=1
1626 true.executable=cat
1626 true.executable=cat
1627 # hg update -C 1
1627 # hg update -C 1
1628 $ cat <<EOF > mytool
1628 $ cat <<EOF > mytool
1629 > echo labellocal: \"\$1\"
1629 > echo labellocal: \"\$1\"
1630 > echo labelother: \"\$2\"
1630 > echo labelother: \"\$2\"
1631 > echo "output (arg)": \"\$3\"
1631 > echo "output (arg)": \"\$3\"
1632 > echo "output (contents)":
1632 > echo "output (contents)":
1633 > cat "\$3"
1633 > cat "\$3"
1634 > EOF
1634 > EOF
1635 $ hg --config merge-tools.true.executable='sh' \
1635 $ hg --config merge-tools.true.executable='sh' \
1636 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1636 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1637 > --config merge-tools.true.premerge=keep \
1637 > --config merge-tools.true.premerge=keep \
1638 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1638 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1639 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1639 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1640 > --config ui.mergemarkers=detailed \
1640 > --config ui.mergemarkers=detailed \
1641 > merge -r 2
1641 > merge -r 2
1642 merging f
1642 merging f
1643 labellocal: "working copy"
1643 labellocal: "working copy"
1644 labelother: "merge rev"
1644 labelother: "merge rev"
1645 output (arg): "$TESTTMP/repo/f"
1645 output (arg): "$TESTTMP/repo/f"
1646 output (contents):
1646 output (contents):
1647 <<<<<<< working copy: uitmpl 1
1647 <<<<<<< working copy: uitmpl 1
1648 revision 1
1648 revision 1
1649 =======
1649 =======
1650 revision 2
1650 revision 2
1651 >>>>>>> merge rev: uitmpl 2
1651 >>>>>>> merge rev: uitmpl 2
1652 space
1652 space
1653 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1653 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1654 (branch merge, don't forget to commit)
1654 (branch merge, don't forget to commit)
1655 $ rm -f 'mytool'
1655 $ rm -f 'mytool'
1656
1656
1657 premerge=keep uses the *tool's* mergemarkertemplate if tool's
1657 premerge=keep uses the *tool's* mergemarkertemplate if tool's
1658 mergemarkers=detailed; labellocal and labelother also use the tool's template
1658 mergemarkers=detailed; labellocal and labelother also use the tool's template
1659
1659
1660 $ beforemerge
1660 $ beforemerge
1661 [merge-tools]
1661 [merge-tools]
1662 false.whatever=
1662 false.whatever=
1663 true.priority=1
1663 true.priority=1
1664 true.executable=cat
1664 true.executable=cat
1665 # hg update -C 1
1665 # hg update -C 1
1666 $ cat <<EOF > mytool
1666 $ cat <<EOF > mytool
1667 > echo labellocal: \"\$1\"
1667 > echo labellocal: \"\$1\"
1668 > echo labelother: \"\$2\"
1668 > echo labelother: \"\$2\"
1669 > echo "output (arg)": \"\$3\"
1669 > echo "output (arg)": \"\$3\"
1670 > echo "output (contents)":
1670 > echo "output (contents)":
1671 > cat "\$3"
1671 > cat "\$3"
1672 > EOF
1672 > EOF
1673 $ hg --config merge-tools.true.executable='sh' \
1673 $ hg --config merge-tools.true.executable='sh' \
1674 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1674 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1675 > --config merge-tools.true.premerge=keep \
1675 > --config merge-tools.true.premerge=keep \
1676 > --config merge-tools.true.mergemarkers=detailed \
1676 > --config merge-tools.true.mergemarkers=detailed \
1677 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1677 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1678 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1678 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1679 > --config ui.mergemarkers=detailed \
1679 > --config ui.mergemarkers=detailed \
1680 > merge -r 2
1680 > merge -r 2
1681 merging f
1681 merging f
1682 labellocal: "working copy: tooltmpl ef83787e2614"
1682 labellocal: "working copy: tooltmpl ef83787e2614"
1683 labelother: "merge rev: tooltmpl 0185f4e0cf02"
1683 labelother: "merge rev: tooltmpl 0185f4e0cf02"
1684 output (arg): "$TESTTMP/repo/f"
1684 output (arg): "$TESTTMP/repo/f"
1685 output (contents):
1685 output (contents):
1686 <<<<<<< working copy: tooltmpl ef83787e2614
1686 <<<<<<< working copy: tooltmpl ef83787e2614
1687 revision 1
1687 revision 1
1688 =======
1688 =======
1689 revision 2
1689 revision 2
1690 >>>>>>> merge rev: tooltmpl 0185f4e0cf02
1690 >>>>>>> merge rev: tooltmpl 0185f4e0cf02
1691 space
1691 space
1692 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1692 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1693 (branch merge, don't forget to commit)
1693 (branch merge, don't forget to commit)
1694 $ rm -f 'mytool'
1694 $ rm -f 'mytool'
1695
1695
1696 Issue3581: Merging a filename that needs to be quoted
1696 Issue3581: Merging a filename that needs to be quoted
1697 (This test doesn't work on Windows filesystems even on Linux, so check
1697 (This test doesn't work on Windows filesystems even on Linux, so check
1698 for Unix-like permission)
1698 for Unix-like permission)
1699
1699
1700 #if unix-permissions
1700 #if unix-permissions
1701 $ beforemerge
1701 $ beforemerge
1702 [merge-tools]
1702 [merge-tools]
1703 false.whatever=
1703 false.whatever=
1704 true.priority=1
1704 true.priority=1
1705 true.executable=cat
1705 true.executable=cat
1706 # hg update -C 1
1706 # hg update -C 1
1707 $ echo "revision 5" > '"; exit 1; echo "'
1707 $ echo "revision 5" > '"; exit 1; echo "'
1708 $ hg commit -Am "revision 5"
1708 $ hg commit -Am "revision 5"
1709 adding "; exit 1; echo "
1709 adding "; exit 1; echo "
1710 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1710 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1711 $ hg update -C 1 > /dev/null
1711 $ hg update -C 1 > /dev/null
1712 $ echo "revision 6" > '"; exit 1; echo "'
1712 $ echo "revision 6" > '"; exit 1; echo "'
1713 $ hg commit -Am "revision 6"
1713 $ hg commit -Am "revision 6"
1714 adding "; exit 1; echo "
1714 adding "; exit 1; echo "
1715 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1715 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1716 created new head
1716 created new head
1717 $ hg merge --config merge-tools.true.executable="true" -r 5
1717 $ hg merge --config merge-tools.true.executable="true" -r 5
1718 merging "; exit 1; echo "
1718 merging "; exit 1; echo "
1719 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1719 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1720 (branch merge, don't forget to commit)
1720 (branch merge, don't forget to commit)
1721 $ hg update -C 1 > /dev/null
1721 $ hg update -C 1 > /dev/null
1722
1722
1723 #else
1723 #else
1724
1724
1725 Match the non-portable filename commits above for test stability
1725 Match the non-portable filename commits above for test stability
1726
1726
1727 $ hg import --bypass -q - << EOF
1727 $ hg import --bypass -q - << EOF
1728 > # HG changeset patch
1728 > # HG changeset patch
1729 > revision 5
1729 > revision 5
1730 >
1730 >
1731 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1731 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1732 > new file mode 100644
1732 > new file mode 100644
1733 > --- /dev/null
1733 > --- /dev/null
1734 > +++ b/"; exit 1; echo "
1734 > +++ b/"; exit 1; echo "
1735 > @@ -0,0 +1,1 @@
1735 > @@ -0,0 +1,1 @@
1736 > +revision 5
1736 > +revision 5
1737 > EOF
1737 > EOF
1738
1738
1739 $ hg import --bypass -q - << EOF
1739 $ hg import --bypass -q - << EOF
1740 > # HG changeset patch
1740 > # HG changeset patch
1741 > revision 6
1741 > revision 6
1742 >
1742 >
1743 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1743 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1744 > new file mode 100644
1744 > new file mode 100644
1745 > --- /dev/null
1745 > --- /dev/null
1746 > +++ b/"; exit 1; echo "
1746 > +++ b/"; exit 1; echo "
1747 > @@ -0,0 +1,1 @@
1747 > @@ -0,0 +1,1 @@
1748 > +revision 6
1748 > +revision 6
1749 > EOF
1749 > EOF
1750
1750
1751 #endif
1751 #endif
1752
1752
1753 Merge post-processing
1753 Merge post-processing
1754
1754
1755 cat is a bad merge-tool and doesn't change:
1755 cat is a bad merge-tool and doesn't change:
1756
1756
1757 $ beforemerge
1757 $ beforemerge
1758 [merge-tools]
1758 [merge-tools]
1759 false.whatever=
1759 false.whatever=
1760 true.priority=1
1760 true.priority=1
1761 true.executable=cat
1761 true.executable=cat
1762 # hg update -C 1
1762 # hg update -C 1
1763 $ hg merge -y -r 2 --config merge-tools.true.checkchanged=1
1763 $ hg merge -y -r 2 --config merge-tools.true.checkchanged=1
1764 merging f
1764 merging f
1765 revision 1
1765 revision 1
1766 space
1766 space
1767 revision 0
1767 revision 0
1768 space
1768 space
1769 revision 2
1769 revision 2
1770 space
1770 space
1771 output file f appears unchanged
1771 output file f appears unchanged
1772 was merge successful (yn)? n
1772 was merge successful (yn)? n
1773 merging f failed!
1773 merging f failed!
1774 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1774 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1775 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1775 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1776 [1]
1776 [1]
1777 $ aftermerge
1777 $ aftermerge
1778 # cat f
1778 # cat f
1779 revision 1
1779 revision 1
1780 space
1780 space
1781 # hg stat
1781 # hg stat
1782 M f
1782 M f
1783 ? f.orig
1783 ? f.orig
1784 # hg resolve --list
1784 # hg resolve --list
1785 U f
1785 U f
1786
1786
1787 missingbinary is a merge-tool that doesn't exist:
1787 missingbinary is a merge-tool that doesn't exist:
1788
1788
1789 $ echo "missingbinary.executable=doesnotexist" >> .hg/hgrc
1789 $ echo "missingbinary.executable=doesnotexist" >> .hg/hgrc
1790 $ beforemerge
1790 $ beforemerge
1791 [merge-tools]
1791 [merge-tools]
1792 false.whatever=
1792 false.whatever=
1793 true.priority=1
1793 true.priority=1
1794 true.executable=cat
1794 true.executable=cat
1795 missingbinary.executable=doesnotexist
1795 missingbinary.executable=doesnotexist
1796 # hg update -C 1
1796 # hg update -C 1
1797 $ hg merge -y -r 2 --config ui.merge=missingbinary
1797 $ hg merge -y -r 2 --config ui.merge=missingbinary
1798 couldn't find merge tool missingbinary (for pattern f)
1798 couldn't find merge tool missingbinary (for pattern f)
1799 merging f
1799 merging f
1800 couldn't find merge tool missingbinary (for pattern f)
1800 couldn't find merge tool missingbinary (for pattern f)
1801 revision 1
1801 revision 1
1802 space
1802 space
1803 revision 0
1803 revision 0
1804 space
1804 space
1805 revision 2
1805 revision 2
1806 space
1806 space
1807 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1807 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1808 (branch merge, don't forget to commit)
1808 (branch merge, don't forget to commit)
1809
1809
1810 $ hg update -q -C 1
1810 $ hg update -q -C 1
1811 $ rm f
1811 $ rm f
1812
1812
1813 internal merge cannot handle symlinks and shouldn't try:
1813 internal merge cannot handle symlinks and shouldn't try:
1814
1814
1815 #if symlink
1815 #if symlink
1816
1816
1817 $ ln -s symlink f
1817 $ ln -s symlink f
1818 $ hg commit -qm 'f is symlink'
1818 $ hg commit -qm 'f is symlink'
1819
1819
1820 #else
1820 #else
1821
1821
1822 $ hg import --bypass -q - << EOF
1822 $ hg import --bypass -q - << EOF
1823 > # HG changeset patch
1823 > # HG changeset patch
1824 > f is symlink
1824 > f is symlink
1825 >
1825 >
1826 > diff --git a/f b/f
1826 > diff --git a/f b/f
1827 > old mode 100644
1827 > old mode 100644
1828 > new mode 120000
1828 > new mode 120000
1829 > --- a/f
1829 > --- a/f
1830 > +++ b/f
1830 > +++ b/f
1831 > @@ -1,2 +1,1 @@
1831 > @@ -1,2 +1,1 @@
1832 > -revision 1
1832 > -revision 1
1833 > -space
1833 > -space
1834 > +symlink
1834 > +symlink
1835 > \ No newline at end of file
1835 > \ No newline at end of file
1836 > EOF
1836 > EOF
1837
1837
1838 Resolve 'other [destination] changed f which local [working copy] deleted' prompt
1838 Resolve 'other [destination] changed f which local [working copy] deleted' prompt
1839 $ hg up -q -C --config ui.interactive=True << EOF
1839 $ hg up -q -C --config ui.interactive=True << EOF
1840 > c
1840 > c
1841 > EOF
1841 > EOF
1842
1842
1843 #endif
1843 #endif
1844
1844
1845 $ hg merge -r 2 --tool internal:merge
1845 $ hg merge -r 2 --tool internal:merge
1846 merging f
1846 merging f
1847 warning: internal :merge cannot merge symlinks for f
1847 warning: internal :merge cannot merge symlinks for f
1848 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
1848 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
1849 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1849 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1850 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1850 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1851 [1]
1851 [1]
1852
1852
1853 Verify naming of temporary files and that extension is preserved:
1853 Verify naming of temporary files and that extension is preserved:
1854
1854
1855 $ hg update -q -C 1
1855 $ hg update -q -C 1
1856 $ hg mv f f.txt
1856 $ hg mv f f.txt
1857 $ hg ci -qm "f.txt"
1857 $ hg ci -qm "f.txt"
1858 $ hg update -q -C 2
1858 $ hg update -q -C 2
1859 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output'
1859 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output'
1860 merging f and f.txt to f.txt
1860 merging f and f.txt to f.txt
1861 */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/repo/f.txt (glob)
1861 */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/repo/f.txt (glob)
1862 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1862 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1863 (branch merge, don't forget to commit)
1863 (branch merge, don't forget to commit)
1864
1864
1865 Verify naming of temporary files and that extension is preserved
1865 Verify naming of temporary files and that extension is preserved
1866 (experimental.mergetempdirprefix version):
1866 (experimental.mergetempdirprefix version):
1867
1867
1868 $ hg update -q -C 1
1868 $ hg update -q -C 1
1869 $ hg mv f f.txt
1869 $ hg mv f f.txt
1870 $ hg ci -qm "f.txt"
1870 $ hg ci -qm "f.txt"
1871 warning: commit already existed in the repository!
1871 $ hg update -q -C 2
1872 $ hg update -q -C 2
1872 $ hg merge -y -r tip --tool echo \
1873 $ hg merge -y -r tip --tool echo \
1873 > --config merge-tools.echo.args='$base $local $other $output' \
1874 > --config merge-tools.echo.args='$base $local $other $output' \
1874 > --config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
1875 > --config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
1875 merging f and f.txt to f.txt
1876 merging f and f.txt to f.txt
1876 $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/repo/f.txt (glob)
1877 $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/repo/f.txt (glob)
1877 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1878 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1878 (branch merge, don't forget to commit)
1879 (branch merge, don't forget to commit)
1879
1880
1880 Binary files capability checking
1881 Binary files capability checking
1881
1882
1882 $ hg update -q -C 0
1883 $ hg update -q -C 0
1883 $ python <<EOF
1884 $ python <<EOF
1884 > with open('b', 'wb') as fp:
1885 > with open('b', 'wb') as fp:
1885 > fp.write(b'\x00\x01\x02\x03')
1886 > fp.write(b'\x00\x01\x02\x03')
1886 > EOF
1887 > EOF
1887 $ hg add b
1888 $ hg add b
1888 $ hg commit -qm "add binary file (#1)"
1889 $ hg commit -qm "add binary file (#1)"
1889
1890
1890 $ hg update -q -C 0
1891 $ hg update -q -C 0
1891 $ python <<EOF
1892 $ python <<EOF
1892 > with open('b', 'wb') as fp:
1893 > with open('b', 'wb') as fp:
1893 > fp.write(b'\x03\x02\x01\x00')
1894 > fp.write(b'\x03\x02\x01\x00')
1894 > EOF
1895 > EOF
1895 $ hg add b
1896 $ hg add b
1896 $ hg commit -qm "add binary file (#2)"
1897 $ hg commit -qm "add binary file (#2)"
1897
1898
1898 By default, binary files capability of internal merge tools is not
1899 By default, binary files capability of internal merge tools is not
1899 checked strictly.
1900 checked strictly.
1900
1901
1901 (for merge-patterns, chosen unintentionally)
1902 (for merge-patterns, chosen unintentionally)
1902
1903
1903 $ hg merge 9 \
1904 $ hg merge 9 \
1904 > --config merge-patterns.b=:merge-other \
1905 > --config merge-patterns.b=:merge-other \
1905 > --config merge-patterns.re:[a-z]=:other
1906 > --config merge-patterns.re:[a-z]=:other
1906 warning: check merge-patterns configurations, if ':merge-other' for binary file 'b' is unintentional
1907 warning: check merge-patterns configurations, if ':merge-other' for binary file 'b' is unintentional
1907 (see 'hg help merge-tools' for binary files capability)
1908 (see 'hg help merge-tools' for binary files capability)
1908 merging b
1909 merging b
1909 warning: b looks like a binary file.
1910 warning: b looks like a binary file.
1910 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1911 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1911 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1912 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1912 [1]
1913 [1]
1913 (Testing that commands.merge.require-rev doesn't break --abort)
1914 (Testing that commands.merge.require-rev doesn't break --abort)
1914 $ hg merge --abort -q
1915 $ hg merge --abort -q
1915
1916
1916 (for ui.merge, ignored unintentionally)
1917 (for ui.merge, ignored unintentionally)
1917
1918
1918 $ hg merge 9 \
1919 $ hg merge 9 \
1919 > --config merge-tools.:other.binary=true \
1920 > --config merge-tools.:other.binary=true \
1920 > --config ui.merge=:other
1921 > --config ui.merge=:other
1921 tool :other (for pattern b) can't handle binary
1922 tool :other (for pattern b) can't handle binary
1922 tool true can't handle binary
1923 tool true can't handle binary
1923 tool :other can't handle binary
1924 tool :other can't handle binary
1924 tool false can't handle binary
1925 tool false can't handle binary
1925 no tool found to merge b
1926 no tool found to merge b
1926 file 'b' needs to be resolved.
1927 file 'b' needs to be resolved.
1927 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
1928 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
1928 What do you want to do? u
1929 What do you want to do? u
1929 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1930 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1930 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1931 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1931 [1]
1932 [1]
1932 $ hg merge --abort -q
1933 $ hg merge --abort -q
1933
1934
1934 With merge.strict-capability-check=true, binary files capability of
1935 With merge.strict-capability-check=true, binary files capability of
1935 internal merge tools is checked strictly.
1936 internal merge tools is checked strictly.
1936
1937
1937 $ f --hexdump b
1938 $ f --hexdump b
1938 b:
1939 b:
1939 0000: 03 02 01 00 |....|
1940 0000: 03 02 01 00 |....|
1940
1941
1941 (for merge-patterns)
1942 (for merge-patterns)
1942
1943
1943 $ hg merge 9 --config merge.strict-capability-check=true \
1944 $ hg merge 9 --config merge.strict-capability-check=true \
1944 > --config merge-tools.:merge-other.binary=true \
1945 > --config merge-tools.:merge-other.binary=true \
1945 > --config merge-patterns.b=:merge-other \
1946 > --config merge-patterns.b=:merge-other \
1946 > --config merge-patterns.re:[a-z]=:other
1947 > --config merge-patterns.re:[a-z]=:other
1947 tool :merge-other (for pattern b) can't handle binary
1948 tool :merge-other (for pattern b) can't handle binary
1948 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1949 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1949 (branch merge, don't forget to commit)
1950 (branch merge, don't forget to commit)
1950 $ f --hexdump b
1951 $ f --hexdump b
1951 b:
1952 b:
1952 0000: 00 01 02 03 |....|
1953 0000: 00 01 02 03 |....|
1953 $ hg merge --abort -q
1954 $ hg merge --abort -q
1954
1955
1955 (for ui.merge)
1956 (for ui.merge)
1956
1957
1957 $ hg merge 9 --config merge.strict-capability-check=true \
1958 $ hg merge 9 --config merge.strict-capability-check=true \
1958 > --config ui.merge=:other
1959 > --config ui.merge=:other
1959 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1960 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1960 (branch merge, don't forget to commit)
1961 (branch merge, don't forget to commit)
1961 $ f --hexdump b
1962 $ f --hexdump b
1962 b:
1963 b:
1963 0000: 00 01 02 03 |....|
1964 0000: 00 01 02 03 |....|
1964 $ hg merge --abort -q
1965 $ hg merge --abort -q
1965
1966
1966 Check that the extra information is printed correctly
1967 Check that the extra information is printed correctly
1967
1968
1968 $ hg merge 9 \
1969 $ hg merge 9 \
1969 > --config merge-tools.testecho.executable='echo' \
1970 > --config merge-tools.testecho.executable='echo' \
1970 > --config merge-tools.testecho.args='merge runs here ...' \
1971 > --config merge-tools.testecho.args='merge runs here ...' \
1971 > --config merge-tools.testecho.binary=True \
1972 > --config merge-tools.testecho.binary=True \
1972 > --config ui.merge=testecho \
1973 > --config ui.merge=testecho \
1973 > --config ui.pre-merge-tool-output-template='\n{label("extmerge.running_merge_tool", "Running merge tool for {path} ({toolpath}):")}\n{separate("\n", extmerge_section(local), extmerge_section(base), extmerge_section(other))}\n' \
1974 > --config ui.pre-merge-tool-output-template='\n{label("extmerge.running_merge_tool", "Running merge tool for {path} ({toolpath}):")}\n{separate("\n", extmerge_section(local), extmerge_section(base), extmerge_section(other))}\n' \
1974 > --config 'templatealias.extmerge_section(sect)="- {pad("{sect.name} ({sect.label})", 20, left=True)}: {revset(sect.node)%"{rev}:{shortest(node,8)} {desc|firstline} {separate(" ", tags, bookmarks, branch)}"}"'
1975 > --config 'templatealias.extmerge_section(sect)="- {pad("{sect.name} ({sect.label})", 20, left=True)}: {revset(sect.node)%"{rev}:{shortest(node,8)} {desc|firstline} {separate(" ", tags, bookmarks, branch)}"}"'
1975 merging b
1976 merging b
1976
1977
1977 Running merge tool for b ("*/bin/echo.exe"): (glob) (windows !)
1978 Running merge tool for b ("*/bin/echo.exe"): (glob) (windows !)
1978 Running merge tool for b (*/bin/echo): (glob) (no-windows !)
1979 Running merge tool for b (*/bin/echo): (glob) (no-windows !)
1979 - local (working copy): 10:2d1f533d add binary file (#2) tip default
1980 - local (working copy): 10:2d1f533d add binary file (#2) tip default
1980 - base (base): -1:00000000 default
1981 - base (base): -1:00000000 default
1981 - other (merge rev): 9:1e7ad7d7 add binary file (#1) default
1982 - other (merge rev): 9:1e7ad7d7 add binary file (#1) default
1982 merge runs here ...
1983 merge runs here ...
1983 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1984 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1984 (branch merge, don't forget to commit)
1985 (branch merge, don't forget to commit)
1985
1986
1986 Check that debugpicktool examines which merge tool is chosen for
1987 Check that debugpicktool examines which merge tool is chosen for
1987 specified file as expected
1988 specified file as expected
1988
1989
1989 $ beforemerge
1990 $ beforemerge
1990 [merge-tools]
1991 [merge-tools]
1991 false.whatever=
1992 false.whatever=
1992 true.priority=1
1993 true.priority=1
1993 true.executable=cat
1994 true.executable=cat
1994 missingbinary.executable=doesnotexist
1995 missingbinary.executable=doesnotexist
1995 # hg update -C 1
1996 # hg update -C 1
1996
1997
1997 (default behavior: checking files in the working parent context)
1998 (default behavior: checking files in the working parent context)
1998
1999
1999 $ hg manifest
2000 $ hg manifest
2000 f
2001 f
2001 $ hg debugpickmergetool
2002 $ hg debugpickmergetool
2002 f = true
2003 f = true
2003
2004
2004 (-X/-I and file patterns limmit examination targets)
2005 (-X/-I and file patterns limmit examination targets)
2005
2006
2006 $ hg debugpickmergetool -X f
2007 $ hg debugpickmergetool -X f
2007 $ hg debugpickmergetool unknown
2008 $ hg debugpickmergetool unknown
2008 unknown: no such file in rev ef83787e2614
2009 unknown: no such file in rev ef83787e2614
2009
2010
2010 (--changedelete emulates merging change and delete)
2011 (--changedelete emulates merging change and delete)
2011
2012
2012 $ hg debugpickmergetool --changedelete
2013 $ hg debugpickmergetool --changedelete
2013 f = :prompt
2014 f = :prompt
2014
2015
2015 (-r REV causes checking files in specified revision)
2016 (-r REV causes checking files in specified revision)
2016
2017
2017 $ hg manifest -r 8
2018 $ hg manifest -r 8
2018 f.txt
2019 f.txt
2019 $ hg debugpickmergetool -r 8
2020 $ hg debugpickmergetool -r 8
2020 f.txt = true
2021 f.txt = true
2021
2022
2022 #if symlink
2023 #if symlink
2023
2024
2024 (symlink causes chosing :prompt)
2025 (symlink causes chosing :prompt)
2025
2026
2026 $ hg debugpickmergetool -r 6d00b3726f6e
2027 $ hg debugpickmergetool -r 6d00b3726f6e
2027 f = :prompt
2028 f = :prompt
2028
2029
2029 (by default, it is assumed that no internal merge tools has symlinks
2030 (by default, it is assumed that no internal merge tools has symlinks
2030 capability)
2031 capability)
2031
2032
2032 $ hg debugpickmergetool \
2033 $ hg debugpickmergetool \
2033 > -r 6d00b3726f6e \
2034 > -r 6d00b3726f6e \
2034 > --config merge-tools.:merge-other.symlink=true \
2035 > --config merge-tools.:merge-other.symlink=true \
2035 > --config merge-patterns.f=:merge-other \
2036 > --config merge-patterns.f=:merge-other \
2036 > --config merge-patterns.re:[f]=:merge-local \
2037 > --config merge-patterns.re:[f]=:merge-local \
2037 > --config merge-patterns.re:[a-z]=:other
2038 > --config merge-patterns.re:[a-z]=:other
2038 f = :prompt
2039 f = :prompt
2039
2040
2040 $ hg debugpickmergetool \
2041 $ hg debugpickmergetool \
2041 > -r 6d00b3726f6e \
2042 > -r 6d00b3726f6e \
2042 > --config merge-tools.:other.symlink=true \
2043 > --config merge-tools.:other.symlink=true \
2043 > --config ui.merge=:other
2044 > --config ui.merge=:other
2044 f = :prompt
2045 f = :prompt
2045
2046
2046 (with strict-capability-check=true, actual symlink capabilities are
2047 (with strict-capability-check=true, actual symlink capabilities are
2047 checked striclty)
2048 checked striclty)
2048
2049
2049 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2050 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2050 > -r 6d00b3726f6e \
2051 > -r 6d00b3726f6e \
2051 > --config merge-tools.:merge-other.symlink=true \
2052 > --config merge-tools.:merge-other.symlink=true \
2052 > --config merge-patterns.f=:merge-other \
2053 > --config merge-patterns.f=:merge-other \
2053 > --config merge-patterns.re:[f]=:merge-local \
2054 > --config merge-patterns.re:[f]=:merge-local \
2054 > --config merge-patterns.re:[a-z]=:other
2055 > --config merge-patterns.re:[a-z]=:other
2055 f = :other
2056 f = :other
2056
2057
2057 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2058 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2058 > -r 6d00b3726f6e \
2059 > -r 6d00b3726f6e \
2059 > --config ui.merge=:other
2060 > --config ui.merge=:other
2060 f = :other
2061 f = :other
2061
2062
2062 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2063 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2063 > -r 6d00b3726f6e \
2064 > -r 6d00b3726f6e \
2064 > --config merge-tools.:merge-other.symlink=true \
2065 > --config merge-tools.:merge-other.symlink=true \
2065 > --config ui.merge=:merge-other
2066 > --config ui.merge=:merge-other
2066 f = :prompt
2067 f = :prompt
2067
2068
2068 #endif
2069 #endif
2069
2070
2070 (--verbose shows some configurations)
2071 (--verbose shows some configurations)
2071
2072
2072 $ hg debugpickmergetool --tool foobar -v
2073 $ hg debugpickmergetool --tool foobar -v
2073 with --tool 'foobar'
2074 with --tool 'foobar'
2074 f = foobar
2075 f = foobar
2075
2076
2076 $ HGMERGE=false hg debugpickmergetool -v
2077 $ HGMERGE=false hg debugpickmergetool -v
2077 with HGMERGE='false'
2078 with HGMERGE='false'
2078 f = false
2079 f = false
2079
2080
2080 $ hg debugpickmergetool --config ui.merge=false -v
2081 $ hg debugpickmergetool --config ui.merge=false -v
2081 with ui.merge='false'
2082 with ui.merge='false'
2082 f = false
2083 f = false
2083
2084
2084 (--debug shows errors detected intermediately)
2085 (--debug shows errors detected intermediately)
2085
2086
2086 $ hg debugpickmergetool --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool --debug f
2087 $ hg debugpickmergetool --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool --debug f
2087 couldn't find merge tool true (for pattern f)
2088 couldn't find merge tool true (for pattern f)
2088 couldn't find merge tool true
2089 couldn't find merge tool true
2089 f = false
2090 f = false
2090
2091
2091 $ cd ..
2092 $ cd ..
@@ -1,1053 +1,1054 b''
1 $ cat > $TESTTMP/hook.sh << 'EOF'
1 $ cat > $TESTTMP/hook.sh << 'EOF'
2 > echo "test-hook-close-phase: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
2 > echo "test-hook-close-phase: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
3 > EOF
3 > EOF
4
4
5 $ cat >> $HGRCPATH << EOF
5 $ cat >> $HGRCPATH << EOF
6 > [extensions]
6 > [extensions]
7 > phasereport=$TESTDIR/testlib/ext-phase-report.py
7 > phasereport=$TESTDIR/testlib/ext-phase-report.py
8 > [hooks]
8 > [hooks]
9 > txnclose-phase.test = sh $TESTTMP/hook.sh
9 > txnclose-phase.test = sh $TESTTMP/hook.sh
10 > EOF
10 > EOF
11
11
12 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
12 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
13 $ mkcommit() {
13 $ mkcommit() {
14 > echo "$1" > "$1"
14 > echo "$1" > "$1"
15 > hg add "$1"
15 > hg add "$1"
16 > message="$1"
16 > message="$1"
17 > shift
17 > shift
18 > hg ci -m "$message" $*
18 > hg ci -m "$message" $*
19 > }
19 > }
20
20
21 $ hg init initialrepo
21 $ hg init initialrepo
22 $ cd initialrepo
22 $ cd initialrepo
23
23
24 Cannot change null revision phase
24 Cannot change null revision phase
25
25
26 $ hg phase --force --secret null
26 $ hg phase --force --secret null
27 abort: cannot change null revision phase
27 abort: cannot change null revision phase
28 [255]
28 [255]
29 $ hg phase null
29 $ hg phase null
30 -1: public
30 -1: public
31
31
32 $ mkcommit A
32 $ mkcommit A
33 test-debug-phase: new rev 0: x -> 1
33 test-debug-phase: new rev 0: x -> 1
34 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
34 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
35
35
36 New commit are draft by default
36 New commit are draft by default
37
37
38 $ hglog
38 $ hglog
39 0 1 A
39 0 1 A
40
40
41 Following commit are draft too
41 Following commit are draft too
42
42
43 $ mkcommit B
43 $ mkcommit B
44 test-debug-phase: new rev 1: x -> 1
44 test-debug-phase: new rev 1: x -> 1
45 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
45 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
46
46
47 $ hglog
47 $ hglog
48 1 1 B
48 1 1 B
49 0 1 A
49 0 1 A
50
50
51 Working directory phase is secret when its parent is secret.
51 Working directory phase is secret when its parent is secret.
52
52
53 $ hg phase --force --secret .
53 $ hg phase --force --secret .
54 test-debug-phase: move rev 0: 1 -> 2
54 test-debug-phase: move rev 0: 1 -> 2
55 test-debug-phase: move rev 1: 1 -> 2
55 test-debug-phase: move rev 1: 1 -> 2
56 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> secret
56 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> secret
57 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> secret
57 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> secret
58 $ hg log -r 'wdir()' -T '{phase}\n'
58 $ hg log -r 'wdir()' -T '{phase}\n'
59 secret
59 secret
60 $ hg log -r 'wdir() and public()' -T '{phase}\n'
60 $ hg log -r 'wdir() and public()' -T '{phase}\n'
61 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
61 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
62 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
62 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
63 secret
63 secret
64
64
65 Working directory phase is draft when its parent is draft.
65 Working directory phase is draft when its parent is draft.
66
66
67 $ hg phase --draft .
67 $ hg phase --draft .
68 test-debug-phase: move rev 1: 2 -> 1
68 test-debug-phase: move rev 1: 2 -> 1
69 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: secret -> draft
69 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: secret -> draft
70 $ hg log -r 'wdir()' -T '{phase}\n'
70 $ hg log -r 'wdir()' -T '{phase}\n'
71 draft
71 draft
72 $ hg log -r 'wdir() and public()' -T '{phase}\n'
72 $ hg log -r 'wdir() and public()' -T '{phase}\n'
73 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
73 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
74 draft
74 draft
75 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
75 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
76
76
77 Working directory phase is secret when a new commit will be created as secret,
77 Working directory phase is secret when a new commit will be created as secret,
78 even if the parent is draft.
78 even if the parent is draft.
79
79
80 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
80 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
81 > --config phases.new-commit='secret'
81 > --config phases.new-commit='secret'
82 secret
82 secret
83
83
84 Working directory phase is draft when its parent is public.
84 Working directory phase is draft when its parent is public.
85
85
86 $ hg phase --public .
86 $ hg phase --public .
87 test-debug-phase: move rev 0: 1 -> 0
87 test-debug-phase: move rev 0: 1 -> 0
88 test-debug-phase: move rev 1: 1 -> 0
88 test-debug-phase: move rev 1: 1 -> 0
89 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
89 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
90 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
90 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
91 $ hg log -r 'wdir()' -T '{phase}\n'
91 $ hg log -r 'wdir()' -T '{phase}\n'
92 draft
92 draft
93 $ hg log -r 'wdir() and public()' -T '{phase}\n'
93 $ hg log -r 'wdir() and public()' -T '{phase}\n'
94 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
94 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
95 draft
95 draft
96 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
96 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
97 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
97 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
98 > --config phases.new-commit='secret'
98 > --config phases.new-commit='secret'
99 secret
99 secret
100
100
101 Draft commit are properly created over public one:
101 Draft commit are properly created over public one:
102
102
103 $ hg phase
103 $ hg phase
104 1: public
104 1: public
105 $ hglog
105 $ hglog
106 1 0 B
106 1 0 B
107 0 0 A
107 0 0 A
108
108
109 $ mkcommit C
109 $ mkcommit C
110 test-debug-phase: new rev 2: x -> 1
110 test-debug-phase: new rev 2: x -> 1
111 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
111 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
112 $ mkcommit D
112 $ mkcommit D
113 test-debug-phase: new rev 3: x -> 1
113 test-debug-phase: new rev 3: x -> 1
114 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
114 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
115
115
116 $ hglog
116 $ hglog
117 3 1 D
117 3 1 D
118 2 1 C
118 2 1 C
119 1 0 B
119 1 0 B
120 0 0 A
120 0 0 A
121
121
122 Test creating changeset as secret
122 Test creating changeset as secret
123
123
124 $ mkcommit E --config phases.new-commit='secret'
124 $ mkcommit E --config phases.new-commit='secret'
125 test-debug-phase: new rev 4: x -> 2
125 test-debug-phase: new rev 4: x -> 2
126 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
126 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
127 $ hglog
127 $ hglog
128 4 2 E
128 4 2 E
129 3 1 D
129 3 1 D
130 2 1 C
130 2 1 C
131 1 0 B
131 1 0 B
132 0 0 A
132 0 0 A
133
133
134 Test the secret property is inherited
134 Test the secret property is inherited
135
135
136 $ mkcommit H
136 $ mkcommit H
137 test-debug-phase: new rev 5: x -> 2
137 test-debug-phase: new rev 5: x -> 2
138 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
138 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
139 $ hglog
139 $ hglog
140 5 2 H
140 5 2 H
141 4 2 E
141 4 2 E
142 3 1 D
142 3 1 D
143 2 1 C
143 2 1 C
144 1 0 B
144 1 0 B
145 0 0 A
145 0 0 A
146
146
147 Even on merge
147 Even on merge
148
148
149 $ hg up -q 1
149 $ hg up -q 1
150 $ mkcommit "B'"
150 $ mkcommit "B'"
151 test-debug-phase: new rev 6: x -> 1
151 test-debug-phase: new rev 6: x -> 1
152 created new head
152 created new head
153 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
153 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
154 $ hglog
154 $ hglog
155 6 1 B'
155 6 1 B'
156 5 2 H
156 5 2 H
157 4 2 E
157 4 2 E
158 3 1 D
158 3 1 D
159 2 1 C
159 2 1 C
160 1 0 B
160 1 0 B
161 0 0 A
161 0 0 A
162 $ hg merge 4 # E
162 $ hg merge 4 # E
163 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 (branch merge, don't forget to commit)
164 (branch merge, don't forget to commit)
165 $ hg phase
165 $ hg phase
166 6: draft
166 6: draft
167 4: secret
167 4: secret
168 $ hg ci -m "merge B' and E"
168 $ hg ci -m "merge B' and E"
169 test-debug-phase: new rev 7: x -> 2
169 test-debug-phase: new rev 7: x -> 2
170 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
170 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
171
171
172 $ hglog
172 $ hglog
173 7 2 merge B' and E
173 7 2 merge B' and E
174 6 1 B'
174 6 1 B'
175 5 2 H
175 5 2 H
176 4 2 E
176 4 2 E
177 3 1 D
177 3 1 D
178 2 1 C
178 2 1 C
179 1 0 B
179 1 0 B
180 0 0 A
180 0 0 A
181
181
182 Test secret changeset are not pushed
182 Test secret changeset are not pushed
183
183
184 $ hg init ../push-dest
184 $ hg init ../push-dest
185 $ cat > ../push-dest/.hg/hgrc << EOF
185 $ cat > ../push-dest/.hg/hgrc << EOF
186 > [phases]
186 > [phases]
187 > publish=False
187 > publish=False
188 > EOF
188 > EOF
189 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
189 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
190 comparing with ../push-dest
190 comparing with ../push-dest
191 searching for changes
191 searching for changes
192 0 public A
192 0 public A
193 1 public B
193 1 public B
194 2 draft C
194 2 draft C
195 3 draft D
195 3 draft D
196 6 draft B'
196 6 draft B'
197 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
197 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
198 comparing with ../push-dest
198 comparing with ../push-dest
199 searching for changes
199 searching for changes
200 0 public A
200 0 public A
201 1 public B
201 1 public B
202 2 draft C
202 2 draft C
203 3 draft D
203 3 draft D
204 6 draft B'
204 6 draft B'
205
205
206 $ hg push ../push-dest -f # force because we push multiple heads
206 $ hg push ../push-dest -f # force because we push multiple heads
207 pushing to ../push-dest
207 pushing to ../push-dest
208 searching for changes
208 searching for changes
209 adding changesets
209 adding changesets
210 adding manifests
210 adding manifests
211 adding file changes
211 adding file changes
212 added 5 changesets with 5 changes to 5 files (+1 heads)
212 added 5 changesets with 5 changes to 5 files (+1 heads)
213 test-debug-phase: new rev 0: x -> 0
213 test-debug-phase: new rev 0: x -> 0
214 test-debug-phase: new rev 1: x -> 0
214 test-debug-phase: new rev 1: x -> 0
215 test-debug-phase: new rev 2: x -> 1
215 test-debug-phase: new rev 2: x -> 1
216 test-debug-phase: new rev 3: x -> 1
216 test-debug-phase: new rev 3: x -> 1
217 test-debug-phase: new rev 4: x -> 1
217 test-debug-phase: new rev 4: x -> 1
218 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
218 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
219 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
219 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
220 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
220 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
221 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
221 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
222 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
222 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
223 $ hglog
223 $ hglog
224 7 2 merge B' and E
224 7 2 merge B' and E
225 6 1 B'
225 6 1 B'
226 5 2 H
226 5 2 H
227 4 2 E
227 4 2 E
228 3 1 D
228 3 1 D
229 2 1 C
229 2 1 C
230 1 0 B
230 1 0 B
231 0 0 A
231 0 0 A
232 $ cd ../push-dest
232 $ cd ../push-dest
233 $ hglog
233 $ hglog
234 4 1 B'
234 4 1 B'
235 3 1 D
235 3 1 D
236 2 1 C
236 2 1 C
237 1 0 B
237 1 0 B
238 0 0 A
238 0 0 A
239
239
240 (Issue3303)
240 (Issue3303)
241 Check that remote secret changeset are ignore when checking creation of remote heads
241 Check that remote secret changeset are ignore when checking creation of remote heads
242
242
243 We add a secret head into the push destination. This secret head shadows a
243 We add a secret head into the push destination. This secret head shadows a
244 visible shared between the initial repo and the push destination.
244 visible shared between the initial repo and the push destination.
245
245
246 $ hg up -q 4 # B'
246 $ hg up -q 4 # B'
247 $ mkcommit Z --config phases.new-commit=secret
247 $ mkcommit Z --config phases.new-commit=secret
248 test-debug-phase: new rev 5: x -> 2
248 test-debug-phase: new rev 5: x -> 2
249 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
249 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
250 $ hg phase .
250 $ hg phase .
251 5: secret
251 5: secret
252
252
253 We now try to push a new public changeset that descend from the common public
253 We now try to push a new public changeset that descend from the common public
254 head shadowed by the remote secret head.
254 head shadowed by the remote secret head.
255
255
256 $ cd ../initialrepo
256 $ cd ../initialrepo
257 $ hg up -q 6 #B'
257 $ hg up -q 6 #B'
258 $ mkcommit I
258 $ mkcommit I
259 test-debug-phase: new rev 8: x -> 1
259 test-debug-phase: new rev 8: x -> 1
260 created new head
260 created new head
261 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
261 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
262 $ hg push ../push-dest
262 $ hg push ../push-dest
263 pushing to ../push-dest
263 pushing to ../push-dest
264 searching for changes
264 searching for changes
265 adding changesets
265 adding changesets
266 adding manifests
266 adding manifests
267 adding file changes
267 adding file changes
268 added 1 changesets with 1 changes to 1 files (+1 heads)
268 added 1 changesets with 1 changes to 1 files (+1 heads)
269 test-debug-phase: new rev 6: x -> 1
269 test-debug-phase: new rev 6: x -> 1
270 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
270 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
271
271
272 :note: The "(+1 heads)" is wrong as we do not had any visible head
272 :note: The "(+1 heads)" is wrong as we do not had any visible head
273
273
274 check that branch cache with "served" filter are properly computed and stored
274 check that branch cache with "served" filter are properly computed and stored
275
275
276 $ ls ../push-dest/.hg/cache/branch2*
276 $ ls ../push-dest/.hg/cache/branch2*
277 ../push-dest/.hg/cache/branch2-base
277 ../push-dest/.hg/cache/branch2-base
278 ../push-dest/.hg/cache/branch2-served
278 ../push-dest/.hg/cache/branch2-served
279 $ cat ../push-dest/.hg/cache/branch2-served
279 $ cat ../push-dest/.hg/cache/branch2-served
280 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
280 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
281 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
281 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
282 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
282 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
283 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
283 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
284 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
284 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
285 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
285 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
286 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
286 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
287 $ ls ../push-dest/.hg/cache/branch2*
287 $ ls ../push-dest/.hg/cache/branch2*
288 ../push-dest/.hg/cache/branch2-base
288 ../push-dest/.hg/cache/branch2-base
289 ../push-dest/.hg/cache/branch2-served
289 ../push-dest/.hg/cache/branch2-served
290 ../push-dest/.hg/cache/branch2-visible
290 ../push-dest/.hg/cache/branch2-visible
291 $ cat ../push-dest/.hg/cache/branch2-served
291 $ cat ../push-dest/.hg/cache/branch2-served
292 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
292 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
293 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
293 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
294 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
294 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
295 $ cat ../push-dest/.hg/cache/branch2-visible
295 $ cat ../push-dest/.hg/cache/branch2-visible
296 6d6770faffce199f1fddd1cf87f6f026138cf061 6
296 6d6770faffce199f1fddd1cf87f6f026138cf061 6
297 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
297 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
298 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
298 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
299 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
299 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
300
300
301
301
302 Restore condition prior extra insertion.
302 Restore condition prior extra insertion.
303 $ hg -q --config extensions.mq= strip .
303 $ hg -q --config extensions.mq= strip .
304 $ hg up -q 7
304 $ hg up -q 7
305 $ cd ..
305 $ cd ..
306
306
307 Test secret changeset are not pull
307 Test secret changeset are not pull
308
308
309 $ hg init pull-dest
309 $ hg init pull-dest
310 $ cd pull-dest
310 $ cd pull-dest
311 $ hg pull ../initialrepo
311 $ hg pull ../initialrepo
312 pulling from ../initialrepo
312 pulling from ../initialrepo
313 requesting all changes
313 requesting all changes
314 adding changesets
314 adding changesets
315 adding manifests
315 adding manifests
316 adding file changes
316 adding file changes
317 added 5 changesets with 5 changes to 5 files (+1 heads)
317 added 5 changesets with 5 changes to 5 files (+1 heads)
318 new changesets 4a2df7238c3b:cf9fe039dfd6
318 new changesets 4a2df7238c3b:cf9fe039dfd6
319 test-debug-phase: new rev 0: x -> 0
319 test-debug-phase: new rev 0: x -> 0
320 test-debug-phase: new rev 1: x -> 0
320 test-debug-phase: new rev 1: x -> 0
321 test-debug-phase: new rev 2: x -> 0
321 test-debug-phase: new rev 2: x -> 0
322 test-debug-phase: new rev 3: x -> 0
322 test-debug-phase: new rev 3: x -> 0
323 test-debug-phase: new rev 4: x -> 0
323 test-debug-phase: new rev 4: x -> 0
324 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
324 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
325 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
325 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
326 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
326 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
327 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
327 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
328 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
328 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
329 (run 'hg heads' to see heads, 'hg merge' to merge)
329 (run 'hg heads' to see heads, 'hg merge' to merge)
330 $ hglog
330 $ hglog
331 4 0 B'
331 4 0 B'
332 3 0 D
332 3 0 D
333 2 0 C
333 2 0 C
334 1 0 B
334 1 0 B
335 0 0 A
335 0 0 A
336 $ cd ..
336 $ cd ..
337
337
338 But secret can still be bundled explicitly
338 But secret can still be bundled explicitly
339
339
340 $ cd initialrepo
340 $ cd initialrepo
341 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
341 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
342 4 changesets found
342 4 changesets found
343 $ cd ..
343 $ cd ..
344
344
345 Test secret changeset are not cloned
345 Test secret changeset are not cloned
346 (during local clone)
346 (during local clone)
347
347
348 $ hg clone -qU initialrepo clone-dest
348 $ hg clone -qU initialrepo clone-dest
349 test-debug-phase: new rev 0: x -> 0
349 test-debug-phase: new rev 0: x -> 0
350 test-debug-phase: new rev 1: x -> 0
350 test-debug-phase: new rev 1: x -> 0
351 test-debug-phase: new rev 2: x -> 0
351 test-debug-phase: new rev 2: x -> 0
352 test-debug-phase: new rev 3: x -> 0
352 test-debug-phase: new rev 3: x -> 0
353 test-debug-phase: new rev 4: x -> 0
353 test-debug-phase: new rev 4: x -> 0
354 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
354 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
355 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
355 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
356 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
356 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
357 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
357 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
358 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
358 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
359 $ hglog -R clone-dest
359 $ hglog -R clone-dest
360 4 0 B'
360 4 0 B'
361 3 0 D
361 3 0 D
362 2 0 C
362 2 0 C
363 1 0 B
363 1 0 B
364 0 0 A
364 0 0 A
365
365
366 Test summary
366 Test summary
367
367
368 $ hg summary -R clone-dest --verbose
368 $ hg summary -R clone-dest --verbose
369 parent: -1:000000000000 (no revision checked out)
369 parent: -1:000000000000 (no revision checked out)
370 branch: default
370 branch: default
371 commit: (clean)
371 commit: (clean)
372 update: 5 new changesets (update)
372 update: 5 new changesets (update)
373 $ hg summary -R initialrepo
373 $ hg summary -R initialrepo
374 parent: 7:17a481b3bccb tip
374 parent: 7:17a481b3bccb tip
375 merge B' and E
375 merge B' and E
376 branch: default
376 branch: default
377 commit: (clean) (secret)
377 commit: (clean) (secret)
378 update: 1 new changesets, 2 branch heads (merge)
378 update: 1 new changesets, 2 branch heads (merge)
379 phases: 3 draft, 3 secret
379 phases: 3 draft, 3 secret
380 $ hg summary -R initialrepo --quiet
380 $ hg summary -R initialrepo --quiet
381 parent: 7:17a481b3bccb tip
381 parent: 7:17a481b3bccb tip
382 update: 1 new changesets, 2 branch heads (merge)
382 update: 1 new changesets, 2 branch heads (merge)
383
383
384 Test revset
384 Test revset
385
385
386 $ cd initialrepo
386 $ cd initialrepo
387 $ hglog -r 'public()'
387 $ hglog -r 'public()'
388 0 0 A
388 0 0 A
389 1 0 B
389 1 0 B
390 $ hglog -r 'draft()'
390 $ hglog -r 'draft()'
391 2 1 C
391 2 1 C
392 3 1 D
392 3 1 D
393 6 1 B'
393 6 1 B'
394 $ hglog -r 'secret()'
394 $ hglog -r 'secret()'
395 4 2 E
395 4 2 E
396 5 2 H
396 5 2 H
397 7 2 merge B' and E
397 7 2 merge B' and E
398
398
399 test that phase are displayed in log at debug level
399 test that phase are displayed in log at debug level
400
400
401 $ hg log --debug
401 $ hg log --debug
402 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
402 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
403 tag: tip
403 tag: tip
404 phase: secret
404 phase: secret
405 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
405 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
406 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
406 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
407 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
407 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
408 user: test
408 user: test
409 date: Thu Jan 01 00:00:00 1970 +0000
409 date: Thu Jan 01 00:00:00 1970 +0000
410 files+: C D E
410 files+: C D E
411 extra: branch=default
411 extra: branch=default
412 description:
412 description:
413 merge B' and E
413 merge B' and E
414
414
415
415
416 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
416 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
417 phase: draft
417 phase: draft
418 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
418 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
419 parent: -1:0000000000000000000000000000000000000000
419 parent: -1:0000000000000000000000000000000000000000
420 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
420 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
421 user: test
421 user: test
422 date: Thu Jan 01 00:00:00 1970 +0000
422 date: Thu Jan 01 00:00:00 1970 +0000
423 files+: B'
423 files+: B'
424 extra: branch=default
424 extra: branch=default
425 description:
425 description:
426 B'
426 B'
427
427
428
428
429 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
429 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
430 phase: secret
430 phase: secret
431 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
431 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
432 parent: -1:0000000000000000000000000000000000000000
432 parent: -1:0000000000000000000000000000000000000000
433 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
433 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
434 user: test
434 user: test
435 date: Thu Jan 01 00:00:00 1970 +0000
435 date: Thu Jan 01 00:00:00 1970 +0000
436 files+: H
436 files+: H
437 extra: branch=default
437 extra: branch=default
438 description:
438 description:
439 H
439 H
440
440
441
441
442 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
442 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
443 phase: secret
443 phase: secret
444 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
444 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
445 parent: -1:0000000000000000000000000000000000000000
445 parent: -1:0000000000000000000000000000000000000000
446 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
446 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
447 user: test
447 user: test
448 date: Thu Jan 01 00:00:00 1970 +0000
448 date: Thu Jan 01 00:00:00 1970 +0000
449 files+: E
449 files+: E
450 extra: branch=default
450 extra: branch=default
451 description:
451 description:
452 E
452 E
453
453
454
454
455 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
455 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
456 phase: draft
456 phase: draft
457 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
457 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
458 parent: -1:0000000000000000000000000000000000000000
458 parent: -1:0000000000000000000000000000000000000000
459 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
459 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
460 user: test
460 user: test
461 date: Thu Jan 01 00:00:00 1970 +0000
461 date: Thu Jan 01 00:00:00 1970 +0000
462 files+: D
462 files+: D
463 extra: branch=default
463 extra: branch=default
464 description:
464 description:
465 D
465 D
466
466
467
467
468 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
468 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
469 phase: draft
469 phase: draft
470 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
470 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
471 parent: -1:0000000000000000000000000000000000000000
471 parent: -1:0000000000000000000000000000000000000000
472 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
472 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
473 user: test
473 user: test
474 date: Thu Jan 01 00:00:00 1970 +0000
474 date: Thu Jan 01 00:00:00 1970 +0000
475 files+: C
475 files+: C
476 extra: branch=default
476 extra: branch=default
477 description:
477 description:
478 C
478 C
479
479
480
480
481 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
481 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
482 phase: public
482 phase: public
483 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
483 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
484 parent: -1:0000000000000000000000000000000000000000
484 parent: -1:0000000000000000000000000000000000000000
485 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
485 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
486 user: test
486 user: test
487 date: Thu Jan 01 00:00:00 1970 +0000
487 date: Thu Jan 01 00:00:00 1970 +0000
488 files+: B
488 files+: B
489 extra: branch=default
489 extra: branch=default
490 description:
490 description:
491 B
491 B
492
492
493
493
494 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
494 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
495 phase: public
495 phase: public
496 parent: -1:0000000000000000000000000000000000000000
496 parent: -1:0000000000000000000000000000000000000000
497 parent: -1:0000000000000000000000000000000000000000
497 parent: -1:0000000000000000000000000000000000000000
498 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
498 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
499 user: test
499 user: test
500 date: Thu Jan 01 00:00:00 1970 +0000
500 date: Thu Jan 01 00:00:00 1970 +0000
501 files+: A
501 files+: A
502 extra: branch=default
502 extra: branch=default
503 description:
503 description:
504 A
504 A
505
505
506
506
507
507
508
508
509 (Issue3707)
509 (Issue3707)
510 test invalid phase name
510 test invalid phase name
511
511
512 $ mkcommit I --config phases.new-commit='babar'
512 $ mkcommit I --config phases.new-commit='babar'
513 transaction abort!
513 transaction abort!
514 rollback completed
514 rollback completed
515 abort: phases.new-commit: not a valid phase name ('babar')
515 abort: phases.new-commit: not a valid phase name ('babar')
516 [255]
516 [255]
517 Test phase command
517 Test phase command
518 ===================
518 ===================
519
519
520 initial picture
520 initial picture
521
521
522 $ hg log -G --template "{rev} {phase} {desc}\n"
522 $ hg log -G --template "{rev} {phase} {desc}\n"
523 @ 7 secret merge B' and E
523 @ 7 secret merge B' and E
524 |\
524 |\
525 | o 6 draft B'
525 | o 6 draft B'
526 | |
526 | |
527 +---o 5 secret H
527 +---o 5 secret H
528 | |
528 | |
529 o | 4 secret E
529 o | 4 secret E
530 | |
530 | |
531 o | 3 draft D
531 o | 3 draft D
532 | |
532 | |
533 o | 2 draft C
533 o | 2 draft C
534 |/
534 |/
535 o 1 public B
535 o 1 public B
536 |
536 |
537 o 0 public A
537 o 0 public A
538
538
539
539
540 display changesets phase
540 display changesets phase
541
541
542 (mixing -r and plain rev specification)
542 (mixing -r and plain rev specification)
543
543
544 $ hg phase 1::4 -r 7
544 $ hg phase 1::4 -r 7
545 1: public
545 1: public
546 2: draft
546 2: draft
547 3: draft
547 3: draft
548 4: secret
548 4: secret
549 7: secret
549 7: secret
550
550
551
551
552 move changeset forward
552 move changeset forward
553
553
554 (with -r option)
554 (with -r option)
555
555
556 $ hg phase --public -r 2
556 $ hg phase --public -r 2
557 test-debug-phase: move rev 2: 1 -> 0
557 test-debug-phase: move rev 2: 1 -> 0
558 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
558 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
559 $ hg log -G --template "{rev} {phase} {desc}\n"
559 $ hg log -G --template "{rev} {phase} {desc}\n"
560 @ 7 secret merge B' and E
560 @ 7 secret merge B' and E
561 |\
561 |\
562 | o 6 draft B'
562 | o 6 draft B'
563 | |
563 | |
564 +---o 5 secret H
564 +---o 5 secret H
565 | |
565 | |
566 o | 4 secret E
566 o | 4 secret E
567 | |
567 | |
568 o | 3 draft D
568 o | 3 draft D
569 | |
569 | |
570 o | 2 public C
570 o | 2 public C
571 |/
571 |/
572 o 1 public B
572 o 1 public B
573 |
573 |
574 o 0 public A
574 o 0 public A
575
575
576
576
577 move changeset backward
577 move changeset backward
578
578
579 (without -r option)
579 (without -r option)
580
580
581 $ hg phase --draft --force 2
581 $ hg phase --draft --force 2
582 test-debug-phase: move rev 2: 0 -> 1
582 test-debug-phase: move rev 2: 0 -> 1
583 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
583 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
584 $ hg log -G --template "{rev} {phase} {desc}\n"
584 $ hg log -G --template "{rev} {phase} {desc}\n"
585 @ 7 secret merge B' and E
585 @ 7 secret merge B' and E
586 |\
586 |\
587 | o 6 draft B'
587 | o 6 draft B'
588 | |
588 | |
589 +---o 5 secret H
589 +---o 5 secret H
590 | |
590 | |
591 o | 4 secret E
591 o | 4 secret E
592 | |
592 | |
593 o | 3 draft D
593 o | 3 draft D
594 | |
594 | |
595 o | 2 draft C
595 o | 2 draft C
596 |/
596 |/
597 o 1 public B
597 o 1 public B
598 |
598 |
599 o 0 public A
599 o 0 public A
600
600
601
601
602 move changeset forward and backward
602 move changeset forward and backward
603
603
604 $ hg phase --draft --force 1::4
604 $ hg phase --draft --force 1::4
605 test-debug-phase: move rev 1: 0 -> 1
605 test-debug-phase: move rev 1: 0 -> 1
606 test-debug-phase: move rev 4: 2 -> 1
606 test-debug-phase: move rev 4: 2 -> 1
607 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
607 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
608 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
608 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
609 $ hg log -G --template "{rev} {phase} {desc}\n"
609 $ hg log -G --template "{rev} {phase} {desc}\n"
610 @ 7 secret merge B' and E
610 @ 7 secret merge B' and E
611 |\
611 |\
612 | o 6 draft B'
612 | o 6 draft B'
613 | |
613 | |
614 +---o 5 secret H
614 +---o 5 secret H
615 | |
615 | |
616 o | 4 draft E
616 o | 4 draft E
617 | |
617 | |
618 o | 3 draft D
618 o | 3 draft D
619 | |
619 | |
620 o | 2 draft C
620 o | 2 draft C
621 |/
621 |/
622 o 1 draft B
622 o 1 draft B
623 |
623 |
624 o 0 public A
624 o 0 public A
625
625
626 test partial failure
626 test partial failure
627
627
628 $ hg phase --public 7
628 $ hg phase --public 7
629 test-debug-phase: move rev 1: 1 -> 0
629 test-debug-phase: move rev 1: 1 -> 0
630 test-debug-phase: move rev 2: 1 -> 0
630 test-debug-phase: move rev 2: 1 -> 0
631 test-debug-phase: move rev 3: 1 -> 0
631 test-debug-phase: move rev 3: 1 -> 0
632 test-debug-phase: move rev 4: 1 -> 0
632 test-debug-phase: move rev 4: 1 -> 0
633 test-debug-phase: move rev 6: 1 -> 0
633 test-debug-phase: move rev 6: 1 -> 0
634 test-debug-phase: move rev 7: 2 -> 0
634 test-debug-phase: move rev 7: 2 -> 0
635 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
635 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
636 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
636 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
637 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
637 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
638 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
638 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
639 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
639 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
640 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
640 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
641 $ hg phase --draft '5 or 7'
641 $ hg phase --draft '5 or 7'
642 test-debug-phase: move rev 5: 2 -> 1
642 test-debug-phase: move rev 5: 2 -> 1
643 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
643 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
644 cannot move 1 changesets to a higher phase, use --force
644 cannot move 1 changesets to a higher phase, use --force
645 phase changed for 1 changesets
645 phase changed for 1 changesets
646 [1]
646 [1]
647 $ hg log -G --template "{rev} {phase} {desc}\n"
647 $ hg log -G --template "{rev} {phase} {desc}\n"
648 @ 7 public merge B' and E
648 @ 7 public merge B' and E
649 |\
649 |\
650 | o 6 public B'
650 | o 6 public B'
651 | |
651 | |
652 +---o 5 draft H
652 +---o 5 draft H
653 | |
653 | |
654 o | 4 public E
654 o | 4 public E
655 | |
655 | |
656 o | 3 public D
656 o | 3 public D
657 | |
657 | |
658 o | 2 public C
658 o | 2 public C
659 |/
659 |/
660 o 1 public B
660 o 1 public B
661 |
661 |
662 o 0 public A
662 o 0 public A
663
663
664
664
665 test complete failure
665 test complete failure
666
666
667 $ hg phase --draft 7
667 $ hg phase --draft 7
668 cannot move 1 changesets to a higher phase, use --force
668 cannot move 1 changesets to a higher phase, use --force
669 no phases changed
669 no phases changed
670 [1]
670 [1]
671
671
672 $ cd ..
672 $ cd ..
673
673
674 test hidden changeset are not cloned as public (issue3935)
674 test hidden changeset are not cloned as public (issue3935)
675
675
676 $ cd initialrepo
676 $ cd initialrepo
677
677
678 (enabling evolution)
678 (enabling evolution)
679 $ cat >> $HGRCPATH << EOF
679 $ cat >> $HGRCPATH << EOF
680 > [experimental]
680 > [experimental]
681 > evolution.createmarkers=True
681 > evolution.createmarkers=True
682 > EOF
682 > EOF
683
683
684 (making a changeset hidden; H in that case)
684 (making a changeset hidden; H in that case)
685 $ hg debugobsolete `hg id --debug -r 5`
685 $ hg debugobsolete `hg id --debug -r 5`
686 1 new obsolescence markers
686 1 new obsolescence markers
687 obsoleted 1 changesets
687 obsoleted 1 changesets
688
688
689 $ cd ..
689 $ cd ..
690 $ hg clone initialrepo clonewithobs
690 $ hg clone initialrepo clonewithobs
691 requesting all changes
691 requesting all changes
692 adding changesets
692 adding changesets
693 adding manifests
693 adding manifests
694 adding file changes
694 adding file changes
695 added 7 changesets with 6 changes to 6 files
695 added 7 changesets with 6 changes to 6 files
696 new changesets 4a2df7238c3b:17a481b3bccb
696 new changesets 4a2df7238c3b:17a481b3bccb
697 test-debug-phase: new rev 0: x -> 0
697 test-debug-phase: new rev 0: x -> 0
698 test-debug-phase: new rev 1: x -> 0
698 test-debug-phase: new rev 1: x -> 0
699 test-debug-phase: new rev 2: x -> 0
699 test-debug-phase: new rev 2: x -> 0
700 test-debug-phase: new rev 3: x -> 0
700 test-debug-phase: new rev 3: x -> 0
701 test-debug-phase: new rev 4: x -> 0
701 test-debug-phase: new rev 4: x -> 0
702 test-debug-phase: new rev 5: x -> 0
702 test-debug-phase: new rev 5: x -> 0
703 test-debug-phase: new rev 6: x -> 0
703 test-debug-phase: new rev 6: x -> 0
704 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
704 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
705 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
705 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
706 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
706 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
707 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
707 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
708 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
708 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
709 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
709 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
710 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
710 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
711 updating to branch default
711 updating to branch default
712 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
712 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
713 $ cd clonewithobs
713 $ cd clonewithobs
714 $ hg log -G --template "{rev} {phase} {desc}\n"
714 $ hg log -G --template "{rev} {phase} {desc}\n"
715 @ 6 public merge B' and E
715 @ 6 public merge B' and E
716 |\
716 |\
717 | o 5 public B'
717 | o 5 public B'
718 | |
718 | |
719 o | 4 public E
719 o | 4 public E
720 | |
720 | |
721 o | 3 public D
721 o | 3 public D
722 | |
722 | |
723 o | 2 public C
723 o | 2 public C
724 |/
724 |/
725 o 1 public B
725 o 1 public B
726 |
726 |
727 o 0 public A
727 o 0 public A
728
728
729
729
730 test verify repo containing hidden changesets, which should not abort just
730 test verify repo containing hidden changesets, which should not abort just
731 because repo.cancopy() is False
731 because repo.cancopy() is False
732
732
733 $ cd ../initialrepo
733 $ cd ../initialrepo
734 $ hg verify
734 $ hg verify
735 checking changesets
735 checking changesets
736 checking manifests
736 checking manifests
737 crosschecking files in changesets and manifests
737 crosschecking files in changesets and manifests
738 checking files
738 checking files
739 checked 8 changesets with 7 changes to 7 files
739 checked 8 changesets with 7 changes to 7 files
740
740
741 $ cd ..
741 $ cd ..
742
742
743 check whether HG_PENDING makes pending changes only in related
743 check whether HG_PENDING makes pending changes only in related
744 repositories visible to an external hook.
744 repositories visible to an external hook.
745
745
746 (emulate a transaction running concurrently by copied
746 (emulate a transaction running concurrently by copied
747 .hg/phaseroots.pending in subsequent test)
747 .hg/phaseroots.pending in subsequent test)
748
748
749 $ cat > $TESTTMP/savepending.sh <<EOF
749 $ cat > $TESTTMP/savepending.sh <<EOF
750 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
750 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
751 > exit 1 # to avoid changing phase for subsequent tests
751 > exit 1 # to avoid changing phase for subsequent tests
752 > EOF
752 > EOF
753 $ cd push-dest
753 $ cd push-dest
754 $ hg phase 6
754 $ hg phase 6
755 6: draft
755 6: draft
756 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
756 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
757 transaction abort!
757 transaction abort!
758 rollback completed
758 rollback completed
759 abort: pretxnclose hook exited with status 1
759 abort: pretxnclose hook exited with status 1
760 [255]
760 [255]
761 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
761 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
762
762
763 (check (in)visibility of phaseroot while transaction running in repo)
763 (check (in)visibility of phaseroot while transaction running in repo)
764
764
765 $ cat > $TESTTMP/checkpending.sh <<EOF
765 $ cat > $TESTTMP/checkpending.sh <<EOF
766 > echo '@initialrepo'
766 > echo '@initialrepo'
767 > hg -R "$TESTTMP/initialrepo" phase 7
767 > hg -R "$TESTTMP/initialrepo" phase 7
768 > echo '@push-dest'
768 > echo '@push-dest'
769 > hg -R "$TESTTMP/push-dest" phase 6
769 > hg -R "$TESTTMP/push-dest" phase 6
770 > exit 1 # to avoid changing phase for subsequent tests
770 > exit 1 # to avoid changing phase for subsequent tests
771 > EOF
771 > EOF
772 $ cd ../initialrepo
772 $ cd ../initialrepo
773 $ hg phase 7
773 $ hg phase 7
774 7: public
774 7: public
775 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
775 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
776 @initialrepo
776 @initialrepo
777 7: secret
777 7: secret
778 @push-dest
778 @push-dest
779 6: draft
779 6: draft
780 transaction abort!
780 transaction abort!
781 rollback completed
781 rollback completed
782 abort: pretxnclose hook exited with status 1
782 abort: pretxnclose hook exited with status 1
783 [255]
783 [255]
784
784
785 Check that pretxnclose-phase hook can control phase movement
785 Check that pretxnclose-phase hook can control phase movement
786
786
787 $ hg phase --force b3325c91a4d9 --secret
787 $ hg phase --force b3325c91a4d9 --secret
788 test-debug-phase: move rev 3: 0 -> 2
788 test-debug-phase: move rev 3: 0 -> 2
789 test-debug-phase: move rev 4: 0 -> 2
789 test-debug-phase: move rev 4: 0 -> 2
790 test-debug-phase: move rev 5: 1 -> 2
790 test-debug-phase: move rev 5: 1 -> 2
791 test-debug-phase: move rev 7: 0 -> 2
791 test-debug-phase: move rev 7: 0 -> 2
792 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
792 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
793 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
793 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
794 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
794 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
795 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
795 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
796 $ hg log -G -T phases
796 $ hg log -G -T phases
797 @ changeset: 7:17a481b3bccb
797 @ changeset: 7:17a481b3bccb
798 |\ tag: tip
798 |\ tag: tip
799 | | phase: secret
799 | | phase: secret
800 | | parent: 6:cf9fe039dfd6
800 | | parent: 6:cf9fe039dfd6
801 | | parent: 4:a603bfb5a83e
801 | | parent: 4:a603bfb5a83e
802 | | user: test
802 | | user: test
803 | | date: Thu Jan 01 00:00:00 1970 +0000
803 | | date: Thu Jan 01 00:00:00 1970 +0000
804 | | summary: merge B' and E
804 | | summary: merge B' and E
805 | |
805 | |
806 | o changeset: 6:cf9fe039dfd6
806 | o changeset: 6:cf9fe039dfd6
807 | | phase: public
807 | | phase: public
808 | | parent: 1:27547f69f254
808 | | parent: 1:27547f69f254
809 | | user: test
809 | | user: test
810 | | date: Thu Jan 01 00:00:00 1970 +0000
810 | | date: Thu Jan 01 00:00:00 1970 +0000
811 | | summary: B'
811 | | summary: B'
812 | |
812 | |
813 o | changeset: 4:a603bfb5a83e
813 o | changeset: 4:a603bfb5a83e
814 | | phase: secret
814 | | phase: secret
815 | | user: test
815 | | user: test
816 | | date: Thu Jan 01 00:00:00 1970 +0000
816 | | date: Thu Jan 01 00:00:00 1970 +0000
817 | | summary: E
817 | | summary: E
818 | |
818 | |
819 o | changeset: 3:b3325c91a4d9
819 o | changeset: 3:b3325c91a4d9
820 | | phase: secret
820 | | phase: secret
821 | | user: test
821 | | user: test
822 | | date: Thu Jan 01 00:00:00 1970 +0000
822 | | date: Thu Jan 01 00:00:00 1970 +0000
823 | | summary: D
823 | | summary: D
824 | |
824 | |
825 o | changeset: 2:f838bfaca5c7
825 o | changeset: 2:f838bfaca5c7
826 |/ phase: public
826 |/ phase: public
827 | user: test
827 | user: test
828 | date: Thu Jan 01 00:00:00 1970 +0000
828 | date: Thu Jan 01 00:00:00 1970 +0000
829 | summary: C
829 | summary: C
830 |
830 |
831 o changeset: 1:27547f69f254
831 o changeset: 1:27547f69f254
832 | phase: public
832 | phase: public
833 | user: test
833 | user: test
834 | date: Thu Jan 01 00:00:00 1970 +0000
834 | date: Thu Jan 01 00:00:00 1970 +0000
835 | summary: B
835 | summary: B
836 |
836 |
837 o changeset: 0:4a2df7238c3b
837 o changeset: 0:4a2df7238c3b
838 phase: public
838 phase: public
839 user: test
839 user: test
840 date: Thu Jan 01 00:00:00 1970 +0000
840 date: Thu Jan 01 00:00:00 1970 +0000
841 summary: A
841 summary: A
842
842
843
843
844 Install a hook that prevent b3325c91a4d9 to become public
844 Install a hook that prevent b3325c91a4d9 to become public
845
845
846 $ cat >> .hg/hgrc << EOF
846 $ cat >> .hg/hgrc << EOF
847 > [hooks]
847 > [hooks]
848 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
848 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
849 > EOF
849 > EOF
850
850
851 Try various actions. only the draft move should succeed
851 Try various actions. only the draft move should succeed
852
852
853 $ hg phase --public b3325c91a4d9
853 $ hg phase --public b3325c91a4d9
854 transaction abort!
854 transaction abort!
855 rollback completed
855 rollback completed
856 abort: pretxnclose-phase.nopublish_D hook exited with status 1
856 abort: pretxnclose-phase.nopublish_D hook exited with status 1
857 [255]
857 [255]
858 $ hg phase --public a603bfb5a83e
858 $ hg phase --public a603bfb5a83e
859 transaction abort!
859 transaction abort!
860 rollback completed
860 rollback completed
861 abort: pretxnclose-phase.nopublish_D hook exited with status 1
861 abort: pretxnclose-phase.nopublish_D hook exited with status 1
862 [255]
862 [255]
863 $ hg phase --draft 17a481b3bccb
863 $ hg phase --draft 17a481b3bccb
864 test-debug-phase: move rev 3: 2 -> 1
864 test-debug-phase: move rev 3: 2 -> 1
865 test-debug-phase: move rev 4: 2 -> 1
865 test-debug-phase: move rev 4: 2 -> 1
866 test-debug-phase: move rev 7: 2 -> 1
866 test-debug-phase: move rev 7: 2 -> 1
867 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
867 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
868 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
868 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
869 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
869 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
870 $ hg phase --public 17a481b3bccb
870 $ hg phase --public 17a481b3bccb
871 transaction abort!
871 transaction abort!
872 rollback completed
872 rollback completed
873 abort: pretxnclose-phase.nopublish_D hook exited with status 1
873 abort: pretxnclose-phase.nopublish_D hook exited with status 1
874 [255]
874 [255]
875
875
876 $ cd ..
876 $ cd ..
877
877
878 Test for the "internal" phase
878 Test for the "internal" phase
879 =============================
879 =============================
880
880
881 Check we deny its usage on older repository
881 Check we deny its usage on older repository
882
882
883 $ hg init no-internal-phase --config format.internal-phase=no
883 $ hg init no-internal-phase --config format.internal-phase=no
884 $ cd no-internal-phase
884 $ cd no-internal-phase
885 $ cat .hg/requires
885 $ cat .hg/requires
886 dotencode
886 dotencode
887 fncache
887 fncache
888 generaldelta
888 generaldelta
889 revlogv1
889 revlogv1
890 sparserevlog
890 sparserevlog
891 store
891 store
892 $ echo X > X
892 $ echo X > X
893 $ hg add X
893 $ hg add X
894 $ hg status
894 $ hg status
895 A X
895 A X
896 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
896 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
897 ** ProgrammingError: this repository does not support the internal phase
897 ** ProgrammingError: this repository does not support the internal phase
898 raise error.ProgrammingError(msg)
898 raise error.ProgrammingError(msg)
899 *ProgrammingError: this repository does not support the internal phase (glob)
899 *ProgrammingError: this repository does not support the internal phase (glob)
900 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" 2>&1 | grep ProgrammingError
900 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" 2>&1 | grep ProgrammingError
901 ** ProgrammingError: this repository does not support the archived phase
901 ** ProgrammingError: this repository does not support the archived phase
902 raise error.ProgrammingError(msg)
902 raise error.ProgrammingError(msg)
903 *ProgrammingError: this repository does not support the archived phase (glob)
903 *ProgrammingError: this repository does not support the archived phase (glob)
904
904
905 $ cd ..
905 $ cd ..
906
906
907 Check it works fine with repository that supports it.
907 Check it works fine with repository that supports it.
908
908
909 $ hg init internal-phase --config format.internal-phase=yes
909 $ hg init internal-phase --config format.internal-phase=yes
910 $ cd internal-phase
910 $ cd internal-phase
911 $ cat .hg/requires
911 $ cat .hg/requires
912 dotencode
912 dotencode
913 fncache
913 fncache
914 generaldelta
914 generaldelta
915 internal-phase
915 internal-phase
916 revlogv1
916 revlogv1
917 sparserevlog
917 sparserevlog
918 store
918 store
919 $ mkcommit A
919 $ mkcommit A
920 test-debug-phase: new rev 0: x -> 1
920 test-debug-phase: new rev 0: x -> 1
921 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
921 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
922
922
923 Commit an internal changesets
923 Commit an internal changesets
924
924
925 $ echo B > B
925 $ echo B > B
926 $ hg add B
926 $ hg add B
927 $ hg status
927 $ hg status
928 A B
928 A B
929 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
929 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
930 test-debug-phase: new rev 1: x -> 96
930 test-debug-phase: new rev 1: x -> 96
931 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
931 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
932
932
933 The changeset is a working parent descendant.
933 The changeset is a working parent descendant.
934 Per the usual visibility rules, it is made visible.
934 Per the usual visibility rules, it is made visible.
935
935
936 $ hg log -G -l 3
936 $ hg log -G -l 3
937 @ changeset: 1:c01c42dffc7f
937 @ changeset: 1:c01c42dffc7f
938 | tag: tip
938 | tag: tip
939 | user: test
939 | user: test
940 | date: Thu Jan 01 00:00:00 1970 +0000
940 | date: Thu Jan 01 00:00:00 1970 +0000
941 | summary: my test internal commit
941 | summary: my test internal commit
942 |
942 |
943 o changeset: 0:4a2df7238c3b
943 o changeset: 0:4a2df7238c3b
944 user: test
944 user: test
945 date: Thu Jan 01 00:00:00 1970 +0000
945 date: Thu Jan 01 00:00:00 1970 +0000
946 summary: A
946 summary: A
947
947
948
948
949 Commit is hidden as expected
949 Commit is hidden as expected
950
950
951 $ hg up 0
951 $ hg up 0
952 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
952 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
953 $ hg log -G
953 $ hg log -G
954 @ changeset: 0:4a2df7238c3b
954 @ changeset: 0:4a2df7238c3b
955 tag: tip
955 tag: tip
956 user: test
956 user: test
957 date: Thu Jan 01 00:00:00 1970 +0000
957 date: Thu Jan 01 00:00:00 1970 +0000
958 summary: A
958 summary: A
959
959
960
960
961 Test for archived phase
961 Test for archived phase
962 -----------------------
962 -----------------------
963
963
964 Commit an archived changesets
964 Commit an archived changesets
965
965
966 $ echo B > B
966 $ echo B > B
967 $ hg add B
967 $ hg add B
968 $ hg status
968 $ hg status
969 A B
969 A B
970 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit"
970 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit"
971 test-debug-phase: new rev 2: x -> 32
971 test-debug-phase: new rev 2: x -> 32
972 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived
972 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived
973
973
974 The changeset is a working parent descendant.
974 The changeset is a working parent descendant.
975 Per the usual visibility rules, it is made visible.
975 Per the usual visibility rules, it is made visible.
976
976
977 $ hg log -G -l 3
977 $ hg log -G -l 3
978 @ changeset: 2:8df5997c3361
978 @ changeset: 2:8df5997c3361
979 | tag: tip
979 | tag: tip
980 | parent: 0:4a2df7238c3b
980 | parent: 0:4a2df7238c3b
981 | user: test
981 | user: test
982 | date: Thu Jan 01 00:00:00 1970 +0000
982 | date: Thu Jan 01 00:00:00 1970 +0000
983 | summary: my test archived commit
983 | summary: my test archived commit
984 |
984 |
985 o changeset: 0:4a2df7238c3b
985 o changeset: 0:4a2df7238c3b
986 user: test
986 user: test
987 date: Thu Jan 01 00:00:00 1970 +0000
987 date: Thu Jan 01 00:00:00 1970 +0000
988 summary: A
988 summary: A
989
989
990
990
991 Commit is hidden as expected
991 Commit is hidden as expected
992
992
993 $ hg up 0
993 $ hg up 0
994 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
994 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
995 $ hg log -G
995 $ hg log -G
996 @ changeset: 0:4a2df7238c3b
996 @ changeset: 0:4a2df7238c3b
997 tag: tip
997 tag: tip
998 user: test
998 user: test
999 date: Thu Jan 01 00:00:00 1970 +0000
999 date: Thu Jan 01 00:00:00 1970 +0000
1000 summary: A
1000 summary: A
1001
1001
1002 $ cd ..
1002 $ cd ..
1003
1003
1004 Recommitting an exact match of a public commit shouldn't change it to
1004 Recommitting an exact match of a public commit shouldn't change it to
1005 draft:
1005 draft:
1006
1006
1007 $ cd initialrepo
1007 $ cd initialrepo
1008 $ hg phase -r 2
1008 $ hg phase -r 2
1009 2: public
1009 2: public
1010 $ hg up -C 1
1010 $ hg up -C 1
1011 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1011 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1012 $ mkcommit C
1012 $ mkcommit C
1013 created new head
1013 warning: commit already existed in the repository!
1014 $ hg phase -r 2
1014 $ hg phase -r 2
1015 2: public
1015 2: public
1016
1016
1017 Same, but for secret:
1017 Same, but for secret:
1018
1018
1019 $ hg up 7
1019 $ hg up 7
1020 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1020 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1021 $ mkcommit F -s
1021 $ mkcommit F -s
1022 test-debug-phase: new rev 8: x -> 2
1022 test-debug-phase: new rev 8: x -> 2
1023 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1023 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1024 $ hg up 7
1024 $ hg up 7
1025 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1025 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1026 $ hg phase
1026 $ hg phase
1027 7: draft
1027 7: draft
1028 $ mkcommit F
1028 $ mkcommit F
1029 test-debug-phase: new rev 8: x -> 2
1029 test-debug-phase: new rev 8: x -> 2
1030 warning: commit already existed in the repository!
1030 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1031 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1031 $ hg phase -r tip
1032 $ hg phase -r tip
1032 8: secret
1033 8: secret
1033
1034
1034 But what about obsoleted changesets?
1035 But what about obsoleted changesets?
1035
1036
1036 $ hg up 4
1037 $ hg up 4
1037 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1038 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1038 $ mkcommit H
1039 $ mkcommit H
1039 test-debug-phase: new rev 5: x -> 2
1040 test-debug-phase: new rev 5: x -> 2
1040 created new head
1041 warning: commit already existed in the repository!
1041 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
1042 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
1042 $ hg phase -r 5
1043 $ hg phase -r 5
1043 5: secret
1044 5: secret
1044 $ hg par
1045 $ hg par
1045 changeset: 5:a030c6be5127
1046 changeset: 5:a030c6be5127
1046 user: test
1047 user: test
1047 date: Thu Jan 01 00:00:00 1970 +0000
1048 date: Thu Jan 01 00:00:00 1970 +0000
1048 obsolete: pruned
1049 obsolete: pruned
1049 summary: H
1050 summary: H
1050
1051
1051 $ hg up tip
1052 $ hg up tip
1052 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1053 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1053 $ cd ..
1054 $ cd ..
@@ -1,208 +1,209 b''
1 #require no-windows
1 #require no-windows
2
2
3 $ . "$TESTDIR/remotefilelog-library.sh"
3 $ . "$TESTDIR/remotefilelog-library.sh"
4
4
5 $ hg init master
5 $ hg init master
6 $ cd master
6 $ cd master
7 $ cat >> .hg/hgrc <<EOF
7 $ cat >> .hg/hgrc <<EOF
8 > [remotefilelog]
8 > [remotefilelog]
9 > server=True
9 > server=True
10 > EOF
10 > EOF
11 $ echo x > x
11 $ echo x > x
12 $ echo y > y
12 $ echo y > y
13 $ echo z > z
13 $ echo z > z
14 $ hg commit -qAm xy
14 $ hg commit -qAm xy
15
15
16 $ cd ..
16 $ cd ..
17
17
18 $ hgcloneshallow ssh://user@dummy/master shallow -q
18 $ hgcloneshallow ssh://user@dummy/master shallow -q
19 3 files fetched over 1 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
19 3 files fetched over 1 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
20 $ cd shallow
20 $ cd shallow
21
21
22 # status
22 # status
23
23
24 $ clearcache
24 $ clearcache
25 $ echo xx > x
25 $ echo xx > x
26 $ echo yy > y
26 $ echo yy > y
27 $ touch a
27 $ touch a
28 $ hg status
28 $ hg status
29 M x
29 M x
30 M y
30 M y
31 ? a
31 ? a
32 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob)
32 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob)
33 $ hg add a
33 $ hg add a
34 $ hg status
34 $ hg status
35 M x
35 M x
36 M y
36 M y
37 A a
37 A a
38
38
39 # diff
39 # diff
40
40
41 $ hg debugrebuilddirstate # fixes dirstate non-determinism
41 $ hg debugrebuilddirstate # fixes dirstate non-determinism
42 $ hg add a
42 $ hg add a
43 $ clearcache
43 $ clearcache
44 $ hg diff
44 $ hg diff
45 diff -r f3d0bb0d1e48 x
45 diff -r f3d0bb0d1e48 x
46 --- a/x* (glob)
46 --- a/x* (glob)
47 +++ b/x* (glob)
47 +++ b/x* (glob)
48 @@ -1,1 +1,1 @@
48 @@ -1,1 +1,1 @@
49 -x
49 -x
50 +xx
50 +xx
51 diff -r f3d0bb0d1e48 y
51 diff -r f3d0bb0d1e48 y
52 --- a/y* (glob)
52 --- a/y* (glob)
53 +++ b/y* (glob)
53 +++ b/y* (glob)
54 @@ -1,1 +1,1 @@
54 @@ -1,1 +1,1 @@
55 -y
55 -y
56 +yy
56 +yy
57 3 files fetched over 1 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
57 3 files fetched over 1 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
58
58
59 # local commit
59 # local commit
60
60
61 $ clearcache
61 $ clearcache
62 $ echo a > a
62 $ echo a > a
63 $ echo xxx > x
63 $ echo xxx > x
64 $ echo yyy > y
64 $ echo yyy > y
65 $ hg commit -m a
65 $ hg commit -m a
66 ? files fetched over 1 fetches - (? misses, 0.00% hit ratio) over *s (glob)
66 ? files fetched over 1 fetches - (? misses, 0.00% hit ratio) over *s (glob)
67
67
68 # local commit where the dirstate is clean -- ensure that we do just one fetch
68 # local commit where the dirstate is clean -- ensure that we do just one fetch
69 # (update to a commit on the server first)
69 # (update to a commit on the server first)
70
70
71 $ hg --config debug.dirstate.delaywrite=1 up 0
71 $ hg --config debug.dirstate.delaywrite=1 up 0
72 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
72 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
73 $ clearcache
73 $ clearcache
74 $ hg debugdirstate
74 $ hg debugdirstate
75 n 644 2 * x (glob)
75 n 644 2 * x (glob)
76 n 644 2 * y (glob)
76 n 644 2 * y (glob)
77 n 644 2 * z (glob)
77 n 644 2 * z (glob)
78 $ echo xxxx > x
78 $ echo xxxx > x
79 $ echo yyyy > y
79 $ echo yyyy > y
80 $ hg commit -m x
80 $ hg commit -m x
81 created new head
81 created new head
82 2 files fetched over 1 fetches - (2 misses, 0.00% hit ratio) over *s (glob)
82 2 files fetched over 1 fetches - (2 misses, 0.00% hit ratio) over *s (glob)
83
83
84 # restore state for future tests
84 # restore state for future tests
85
85
86 $ hg -q strip .
86 $ hg -q strip .
87 $ hg -q up tip
87 $ hg -q up tip
88
88
89 # rebase
89 # rebase
90
90
91 $ clearcache
91 $ clearcache
92 $ cd ../master
92 $ cd ../master
93 $ echo w > w
93 $ echo w > w
94 $ hg commit -qAm w
94 $ hg commit -qAm w
95
95
96 $ cd ../shallow
96 $ cd ../shallow
97 $ hg pull
97 $ hg pull
98 pulling from ssh://user@dummy/master
98 pulling from ssh://user@dummy/master
99 searching for changes
99 searching for changes
100 adding changesets
100 adding changesets
101 adding manifests
101 adding manifests
102 adding file changes
102 adding file changes
103 added 1 changesets with 0 changes to 0 files (+1 heads)
103 added 1 changesets with 0 changes to 0 files (+1 heads)
104 new changesets fed61014d323
104 new changesets fed61014d323
105 (run 'hg heads' to see heads, 'hg merge' to merge)
105 (run 'hg heads' to see heads, 'hg merge' to merge)
106
106
107 $ hg rebase -d tip
107 $ hg rebase -d tip
108 rebasing 1:9abfe7bca547 "a"
108 rebasing 1:9abfe7bca547 "a"
109 saved backup bundle to $TESTTMP/shallow/.hg/strip-backup/9abfe7bca547-8b11e5ff-rebase.hg (glob)
109 saved backup bundle to $TESTTMP/shallow/.hg/strip-backup/9abfe7bca547-8b11e5ff-rebase.hg (glob)
110 3 files fetched over 2 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
110 3 files fetched over 2 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
111
111
112 # strip
112 # strip
113
113
114 $ clearcache
114 $ clearcache
115 $ hg debugrebuilddirstate # fixes dirstate non-determinism
115 $ hg debugrebuilddirstate # fixes dirstate non-determinism
116 $ hg strip -r .
116 $ hg strip -r .
117 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
118 saved backup bundle to $TESTTMP/shallow/.hg/strip-backup/19edf50f4de7-df3d0f74-backup.hg (glob)
118 saved backup bundle to $TESTTMP/shallow/.hg/strip-backup/19edf50f4de7-df3d0f74-backup.hg (glob)
119 4 files fetched over 2 fetches - (4 misses, 0.00% hit ratio) over *s (glob)
119 4 files fetched over 2 fetches - (4 misses, 0.00% hit ratio) over *s (glob)
120
120
121 # unbundle
121 # unbundle
122
122
123 $ clearcache
123 $ clearcache
124 $ ls -A
124 $ ls -A
125 .hg
125 .hg
126 w
126 w
127 x
127 x
128 y
128 y
129 z
129 z
130
130
131 $ hg debugrebuilddirstate # fixes dirstate non-determinism
131 $ hg debugrebuilddirstate # fixes dirstate non-determinism
132 $ hg unbundle .hg/strip-backup/19edf50f4de7-df3d0f74-backup.hg
132 $ hg unbundle .hg/strip-backup/19edf50f4de7-df3d0f74-backup.hg
133 adding changesets
133 adding changesets
134 adding manifests
134 adding manifests
135 adding file changes
135 adding file changes
136 added 1 changesets with 0 changes to 0 files
136 added 1 changesets with 0 changes to 0 files
137 new changesets 19edf50f4de7 (1 drafts)
137 new changesets 19edf50f4de7 (1 drafts)
138 (run 'hg update' to get a working copy)
138 (run 'hg update' to get a working copy)
139
139
140 $ hg up
140 $ hg up
141 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 4 files fetched over 1 fetches - (4 misses, 0.00% hit ratio) over *s (glob)
142 4 files fetched over 1 fetches - (4 misses, 0.00% hit ratio) over *s (glob)
143 $ cat a
143 $ cat a
144 a
144 a
145
145
146 # revert
146 # revert
147
147
148 $ clearcache
148 $ clearcache
149 $ hg revert -r .~2 y z
149 $ hg revert -r .~2 y z
150 no changes needed to z
150 no changes needed to z
151 2 files fetched over 2 fetches - (2 misses, 0.00% hit ratio) over *s (glob)
151 2 files fetched over 2 fetches - (2 misses, 0.00% hit ratio) over *s (glob)
152 $ hg checkout -C -r . -q
152 $ hg checkout -C -r . -q
153
153
154 # explicit bundle should produce full bundle file
154 # explicit bundle should produce full bundle file
155
155
156 $ hg bundle -r 2 --base 1 ../local.bundle
156 $ hg bundle -r 2 --base 1 ../local.bundle
157 1 changesets found
157 1 changesets found
158 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob)
158 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob)
159 $ cd ..
159 $ cd ..
160
160
161 $ hgcloneshallow ssh://user@dummy/master shallow2 -q
161 $ hgcloneshallow ssh://user@dummy/master shallow2 -q
162 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob)
162 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over *s (glob)
163 $ cd shallow2
163 $ cd shallow2
164 $ hg unbundle ../local.bundle
164 $ hg unbundle ../local.bundle
165 adding changesets
165 adding changesets
166 adding manifests
166 adding manifests
167 adding file changes
167 adding file changes
168 added 1 changesets with 3 changes to 3 files
168 added 1 changesets with 3 changes to 3 files
169 new changesets 19edf50f4de7 (1 drafts)
169 new changesets 19edf50f4de7 (1 drafts)
170 (run 'hg update' to get a working copy)
170 (run 'hg update' to get a working copy)
171
171
172 $ hg log -r 2 --stat
172 $ hg log -r 2 --stat
173 changeset: 2:19edf50f4de7
173 changeset: 2:19edf50f4de7
174 tag: tip
174 tag: tip
175 user: test
175 user: test
176 date: Thu Jan 01 00:00:00 1970 +0000
176 date: Thu Jan 01 00:00:00 1970 +0000
177 summary: a
177 summary: a
178
178
179 a | 1 +
179 a | 1 +
180 x | 2 +-
180 x | 2 +-
181 y | 2 +-
181 y | 2 +-
182 3 files changed, 3 insertions(+), 2 deletions(-)
182 3 files changed, 3 insertions(+), 2 deletions(-)
183
183
184 # Merge
184 # Merge
185
185
186 $ echo merge >> w
186 $ echo merge >> w
187 $ hg commit -m w
187 $ hg commit -m w
188 created new head
188 created new head
189 $ hg merge 2
189 $ hg merge 2
190 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 (branch merge, don't forget to commit)
191 (branch merge, don't forget to commit)
192 $ hg commit -m merge
192 $ hg commit -m merge
193 $ hg strip -q -r ".^"
193 $ hg strip -q -r ".^"
194
194
195 # commit without producing new node
195 # commit without producing new node
196
196
197 $ cd $TESTTMP
197 $ cd $TESTTMP
198 $ hgcloneshallow ssh://user@dummy/master shallow3 -q
198 $ hgcloneshallow ssh://user@dummy/master shallow3 -q
199 $ cd shallow3
199 $ cd shallow3
200 $ echo 1 > A
200 $ echo 1 > A
201 $ hg commit -m foo -A A
201 $ hg commit -m foo -A A
202 $ hg log -r . -T '{node}\n'
202 $ hg log -r . -T '{node}\n'
203 383ce605500277f879b7460a16ba620eb6930b7f
203 383ce605500277f879b7460a16ba620eb6930b7f
204 $ hg update -r '.^' -q
204 $ hg update -r '.^' -q
205 $ echo 1 > A
205 $ echo 1 > A
206 $ hg commit -m foo -A A
206 $ hg commit -m foo -A A
207 warning: commit already existed in the repository!
207 $ hg log -r . -T '{node}\n'
208 $ hg log -r . -T '{node}\n'
208 383ce605500277f879b7460a16ba620eb6930b7f
209 383ce605500277f879b7460a16ba620eb6930b7f
@@ -1,419 +1,421 b''
1 Test for command `hg unamend` which lives in uncommit extension
1 Test for command `hg unamend` which lives in uncommit extension
2 ===============================================================
2 ===============================================================
3
3
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [alias]
5 > [alias]
6 > glog = log -G -T '{rev}:{node|short} {desc}'
6 > glog = log -G -T '{rev}:{node|short} {desc}'
7 > [experimental]
7 > [experimental]
8 > evolution = createmarkers, allowunstable
8 > evolution = createmarkers, allowunstable
9 > [extensions]
9 > [extensions]
10 > rebase =
10 > rebase =
11 > amend =
11 > amend =
12 > uncommit =
12 > uncommit =
13 > EOF
13 > EOF
14
14
15 Repo Setup
15 Repo Setup
16
16
17 $ hg init repo
17 $ hg init repo
18 $ cd repo
18 $ cd repo
19 $ for ch in a b c d e f g h; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
19 $ for ch in a b c d e f g h; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
20
20
21 $ hg glog
21 $ hg glog
22 @ 7:ec2426147f0e Added h
22 @ 7:ec2426147f0e Added h
23 |
23 |
24 o 6:87d6d6676308 Added g
24 o 6:87d6d6676308 Added g
25 |
25 |
26 o 5:825660c69f0c Added f
26 o 5:825660c69f0c Added f
27 |
27 |
28 o 4:aa98ab95a928 Added e
28 o 4:aa98ab95a928 Added e
29 |
29 |
30 o 3:62615734edd5 Added d
30 o 3:62615734edd5 Added d
31 |
31 |
32 o 2:28ad74487de9 Added c
32 o 2:28ad74487de9 Added c
33 |
33 |
34 o 1:29becc82797a Added b
34 o 1:29becc82797a Added b
35 |
35 |
36 o 0:18d04c59bb5d Added a
36 o 0:18d04c59bb5d Added a
37
37
38 Trying to unamend when there was no amend done
38 Trying to unamend when there was no amend done
39
39
40 $ hg unamend
40 $ hg unamend
41 abort: changeset must have one predecessor, found 0 predecessors
41 abort: changeset must have one predecessor, found 0 predecessors
42 [255]
42 [255]
43
43
44 Unamend on clean wdir and tip
44 Unamend on clean wdir and tip
45
45
46 $ echo "bar" >> h
46 $ echo "bar" >> h
47 $ hg amend
47 $ hg amend
48
48
49 $ hg exp
49 $ hg exp
50 # HG changeset patch
50 # HG changeset patch
51 # User test
51 # User test
52 # Date 0 0
52 # Date 0 0
53 # Thu Jan 01 00:00:00 1970 +0000
53 # Thu Jan 01 00:00:00 1970 +0000
54 # Node ID c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
54 # Node ID c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
55 # Parent 87d6d66763085b629e6d7ed56778c79827273022
55 # Parent 87d6d66763085b629e6d7ed56778c79827273022
56 Added h
56 Added h
57
57
58 diff -r 87d6d6676308 -r c9fa1a715c1b h
58 diff -r 87d6d6676308 -r c9fa1a715c1b h
59 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
59 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
60 +++ b/h Thu Jan 01 00:00:00 1970 +0000
60 +++ b/h Thu Jan 01 00:00:00 1970 +0000
61 @@ -0,0 +1,2 @@
61 @@ -0,0 +1,2 @@
62 +foo
62 +foo
63 +bar
63 +bar
64
64
65 $ hg glog --hidden
65 $ hg glog --hidden
66 @ 8:c9fa1a715c1b Added h
66 @ 8:c9fa1a715c1b Added h
67 |
67 |
68 | x 7:ec2426147f0e Added h
68 | x 7:ec2426147f0e Added h
69 |/
69 |/
70 o 6:87d6d6676308 Added g
70 o 6:87d6d6676308 Added g
71 |
71 |
72 o 5:825660c69f0c Added f
72 o 5:825660c69f0c Added f
73 |
73 |
74 o 4:aa98ab95a928 Added e
74 o 4:aa98ab95a928 Added e
75 |
75 |
76 o 3:62615734edd5 Added d
76 o 3:62615734edd5 Added d
77 |
77 |
78 o 2:28ad74487de9 Added c
78 o 2:28ad74487de9 Added c
79 |
79 |
80 o 1:29becc82797a Added b
80 o 1:29becc82797a Added b
81 |
81 |
82 o 0:18d04c59bb5d Added a
82 o 0:18d04c59bb5d Added a
83
83
84 $ hg unamend
84 $ hg unamend
85 $ hg glog --hidden
85 $ hg glog --hidden
86 @ 9:46d02d47eec6 Added h
86 @ 9:46d02d47eec6 Added h
87 |
87 |
88 | x 8:c9fa1a715c1b Added h
88 | x 8:c9fa1a715c1b Added h
89 |/
89 |/
90 | x 7:ec2426147f0e Added h
90 | x 7:ec2426147f0e Added h
91 |/
91 |/
92 o 6:87d6d6676308 Added g
92 o 6:87d6d6676308 Added g
93 |
93 |
94 o 5:825660c69f0c Added f
94 o 5:825660c69f0c Added f
95 |
95 |
96 o 4:aa98ab95a928 Added e
96 o 4:aa98ab95a928 Added e
97 |
97 |
98 o 3:62615734edd5 Added d
98 o 3:62615734edd5 Added d
99 |
99 |
100 o 2:28ad74487de9 Added c
100 o 2:28ad74487de9 Added c
101 |
101 |
102 o 1:29becc82797a Added b
102 o 1:29becc82797a Added b
103 |
103 |
104 o 0:18d04c59bb5d Added a
104 o 0:18d04c59bb5d Added a
105
105
106 $ hg diff
106 $ hg diff
107 diff -r 46d02d47eec6 h
107 diff -r 46d02d47eec6 h
108 --- a/h Thu Jan 01 00:00:00 1970 +0000
108 --- a/h Thu Jan 01 00:00:00 1970 +0000
109 +++ b/h Thu Jan 01 00:00:00 1970 +0000
109 +++ b/h Thu Jan 01 00:00:00 1970 +0000
110 @@ -1,1 +1,2 @@
110 @@ -1,1 +1,2 @@
111 foo
111 foo
112 +bar
112 +bar
113
113
114 $ hg exp
114 $ hg exp
115 # HG changeset patch
115 # HG changeset patch
116 # User test
116 # User test
117 # Date 0 0
117 # Date 0 0
118 # Thu Jan 01 00:00:00 1970 +0000
118 # Thu Jan 01 00:00:00 1970 +0000
119 # Node ID 46d02d47eec6ca096b8dcab3f8f5579c40c3dd9a
119 # Node ID 46d02d47eec6ca096b8dcab3f8f5579c40c3dd9a
120 # Parent 87d6d66763085b629e6d7ed56778c79827273022
120 # Parent 87d6d66763085b629e6d7ed56778c79827273022
121 Added h
121 Added h
122
122
123 diff -r 87d6d6676308 -r 46d02d47eec6 h
123 diff -r 87d6d6676308 -r 46d02d47eec6 h
124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 +++ b/h Thu Jan 01 00:00:00 1970 +0000
125 +++ b/h Thu Jan 01 00:00:00 1970 +0000
126 @@ -0,0 +1,1 @@
126 @@ -0,0 +1,1 @@
127 +foo
127 +foo
128
128
129 $ hg status
129 $ hg status
130 M h
130 M h
131
131
132 $ hg log -r . -T '{extras % "{extra}\n"}' --config alias.log=log
132 $ hg log -r . -T '{extras % "{extra}\n"}' --config alias.log=log
133 branch=default
133 branch=default
134 unamend_source=c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
134 unamend_source=c9fa1a715c1b7661c0fafb362a9f30bd75878d7d
135
135
136 Using unamend to undo an unamed (intentional)
136 Using unamend to undo an unamed (intentional)
137
137
138 $ hg unamend
138 $ hg unamend
139 $ hg exp
139 $ hg exp
140 # HG changeset patch
140 # HG changeset patch
141 # User test
141 # User test
142 # Date 0 0
142 # Date 0 0
143 # Thu Jan 01 00:00:00 1970 +0000
143 # Thu Jan 01 00:00:00 1970 +0000
144 # Node ID 850ddfc1bc662997ec6094ada958f01f0cc8070a
144 # Node ID 850ddfc1bc662997ec6094ada958f01f0cc8070a
145 # Parent 87d6d66763085b629e6d7ed56778c79827273022
145 # Parent 87d6d66763085b629e6d7ed56778c79827273022
146 Added h
146 Added h
147
147
148 diff -r 87d6d6676308 -r 850ddfc1bc66 h
148 diff -r 87d6d6676308 -r 850ddfc1bc66 h
149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
150 +++ b/h Thu Jan 01 00:00:00 1970 +0000
150 +++ b/h Thu Jan 01 00:00:00 1970 +0000
151 @@ -0,0 +1,2 @@
151 @@ -0,0 +1,2 @@
152 +foo
152 +foo
153 +bar
153 +bar
154 $ hg diff
154 $ hg diff
155
155
156 Unamend on a dirty working directory
156 Unamend on a dirty working directory
157
157
158 $ echo "bar" >> a
158 $ echo "bar" >> a
159 $ hg amend
159 $ hg amend
160 $ echo "foobar" >> a
160 $ echo "foobar" >> a
161 $ echo "bar" >> b
161 $ echo "bar" >> b
162 $ hg status
162 $ hg status
163 M a
163 M a
164 M b
164 M b
165
165
166 $ hg unamend
166 $ hg unamend
167
167
168 $ hg status
168 $ hg status
169 M a
169 M a
170 M b
170 M b
171
171
172 $ hg diff
172 $ hg diff
173 diff -r ec338db45d51 a
173 diff -r ec338db45d51 a
174 --- a/a Thu Jan 01 00:00:00 1970 +0000
174 --- a/a Thu Jan 01 00:00:00 1970 +0000
175 +++ b/a Thu Jan 01 00:00:00 1970 +0000
175 +++ b/a Thu Jan 01 00:00:00 1970 +0000
176 @@ -1,1 +1,3 @@
176 @@ -1,1 +1,3 @@
177 foo
177 foo
178 +bar
178 +bar
179 +foobar
179 +foobar
180 diff -r ec338db45d51 b
180 diff -r ec338db45d51 b
181 --- a/b Thu Jan 01 00:00:00 1970 +0000
181 --- a/b Thu Jan 01 00:00:00 1970 +0000
182 +++ b/b Thu Jan 01 00:00:00 1970 +0000
182 +++ b/b Thu Jan 01 00:00:00 1970 +0000
183 @@ -1,1 +1,2 @@
183 @@ -1,1 +1,2 @@
184 foo
184 foo
185 +bar
185 +bar
186
186
187 Unamending an added file
187 Unamending an added file
188
188
189 $ hg ci -m "Added things to a and b"
189 $ hg ci -m "Added things to a and b"
190 $ echo foo > bar
190 $ echo foo > bar
191 $ hg add bar
191 $ hg add bar
192 $ hg amend
192 $ hg amend
193
193
194 $ hg unamend
194 $ hg unamend
195 $ hg status
195 $ hg status
196 A bar
196 A bar
197
197
198 $ hg revert --all
198 $ hg revert --all
199 forgetting bar
199 forgetting bar
200
200
201 Unamending a removed file
201 Unamending a removed file
202
202
203 $ hg remove a
203 $ hg remove a
204 $ hg amend
204 $ hg amend
205
205
206 $ hg unamend
206 $ hg unamend
207 $ hg status
207 $ hg status
208 R a
208 R a
209 ? bar
209 ? bar
210
210
211 $ hg revert --all
211 $ hg revert --all
212 undeleting a
212 undeleting a
213
213
214 Unamending an added file with dirty wdir status
214 Unamending an added file with dirty wdir status
215
215
216 $ hg add bar
216 $ hg add bar
217 $ hg amend
217 $ hg amend
218 $ echo bar >> bar
218 $ echo bar >> bar
219 $ hg status
219 $ hg status
220 M bar
220 M bar
221
221
222 $ hg unamend
222 $ hg unamend
223 $ hg status
223 $ hg status
224 A bar
224 A bar
225 $ hg diff
225 $ hg diff
226 diff -r 7f79409af972 bar
226 diff -r 7f79409af972 bar
227 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
227 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
228 +++ b/bar Thu Jan 01 00:00:00 1970 +0000
228 +++ b/bar Thu Jan 01 00:00:00 1970 +0000
229 @@ -0,0 +1,2 @@
229 @@ -0,0 +1,2 @@
230 +foo
230 +foo
231 +bar
231 +bar
232
232
233 $ hg revert --all
233 $ hg revert --all
234 forgetting bar
234 forgetting bar
235 $ rm bar
235 $ rm bar
236
236
237 Unamending in middle of a stack
237 Unamending in middle of a stack
238
238
239 $ hg glog
239 $ hg glog
240 @ 19:7f79409af972 Added things to a and b
240 @ 19:7f79409af972 Added things to a and b
241 |
241 |
242 o 12:ec338db45d51 Added h
242 o 12:ec338db45d51 Added h
243 |
243 |
244 o 6:87d6d6676308 Added g
244 o 6:87d6d6676308 Added g
245 |
245 |
246 o 5:825660c69f0c Added f
246 o 5:825660c69f0c Added f
247 |
247 |
248 o 4:aa98ab95a928 Added e
248 o 4:aa98ab95a928 Added e
249 |
249 |
250 o 3:62615734edd5 Added d
250 o 3:62615734edd5 Added d
251 |
251 |
252 o 2:28ad74487de9 Added c
252 o 2:28ad74487de9 Added c
253 |
253 |
254 o 1:29becc82797a Added b
254 o 1:29becc82797a Added b
255 |
255 |
256 o 0:18d04c59bb5d Added a
256 o 0:18d04c59bb5d Added a
257
257
258 $ hg up 5
258 $ hg up 5
259 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
259 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
260 $ echo bar >> f
260 $ echo bar >> f
261 $ hg amend
261 $ hg amend
262 3 new orphan changesets
262 3 new orphan changesets
263 $ hg rebase -s 6 -d . -q
263 $ hg rebase -s 6 -d . -q
264
264
265 $ hg glog
265 $ hg glog
266 o 23:03ddd6fc5af1 Added things to a and b
266 o 23:03ddd6fc5af1 Added things to a and b
267 |
267 |
268 o 22:3e7b64ee157b Added h
268 o 22:3e7b64ee157b Added h
269 |
269 |
270 o 21:49635b68477e Added g
270 o 21:49635b68477e Added g
271 |
271 |
272 @ 20:93f0e8ffab32 Added f
272 @ 20:93f0e8ffab32 Added f
273 |
273 |
274 o 4:aa98ab95a928 Added e
274 o 4:aa98ab95a928 Added e
275 |
275 |
276 o 3:62615734edd5 Added d
276 o 3:62615734edd5 Added d
277 |
277 |
278 o 2:28ad74487de9 Added c
278 o 2:28ad74487de9 Added c
279 |
279 |
280 o 1:29becc82797a Added b
280 o 1:29becc82797a Added b
281 |
281 |
282 o 0:18d04c59bb5d Added a
282 o 0:18d04c59bb5d Added a
283
283
284
284
285 $ hg --config experimental.evolution=createmarkers unamend
285 $ hg --config experimental.evolution=createmarkers unamend
286 abort: cannot unamend changeset with children
286 abort: cannot unamend changeset with children
287 [255]
287 [255]
288
288
289 $ hg unamend
289 $ hg unamend
290 3 new orphan changesets
290 3 new orphan changesets
291
291
292 Trying to unamend a public changeset
292 Trying to unamend a public changeset
293
293
294 $ hg up -C 23
294 $ hg up -C 23
295 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 $ hg phase -r . -p
296 $ hg phase -r . -p
297 1 new phase-divergent changesets
297 1 new phase-divergent changesets
298 $ hg unamend
298 $ hg unamend
299 abort: cannot unamend public changesets
299 abort: cannot unamend public changesets
300 (see 'hg help phases' for details)
300 (see 'hg help phases' for details)
301 [255]
301 [255]
302
302
303 Testing whether unamend retains copies or not
303 Testing whether unamend retains copies or not
304
304
305 $ hg status
305 $ hg status
306
306
307 $ hg mv a foo
307 $ hg mv a foo
308
308
309 $ hg ci -m "Moved a to foo"
309 $ hg ci -m "Moved a to foo"
310 $ hg exp --git
310 $ hg exp --git
311 # HG changeset patch
311 # HG changeset patch
312 # User test
312 # User test
313 # Date 0 0
313 # Date 0 0
314 # Thu Jan 01 00:00:00 1970 +0000
314 # Thu Jan 01 00:00:00 1970 +0000
315 # Node ID cfef290346fbee5126313d7e1aab51d877679b09
315 # Node ID cfef290346fbee5126313d7e1aab51d877679b09
316 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
316 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
317 Moved a to foo
317 Moved a to foo
318
318
319 diff --git a/a b/foo
319 diff --git a/a b/foo
320 rename from a
320 rename from a
321 rename to foo
321 rename to foo
322
322
323 $ hg mv b foobar
323 $ hg mv b foobar
324 $ hg diff --git
324 $ hg diff --git
325 diff --git a/b b/foobar
325 diff --git a/b b/foobar
326 rename from b
326 rename from b
327 rename to foobar
327 rename to foobar
328 $ hg amend
328 $ hg amend
329
329
330 $ hg exp --git
330 $ hg exp --git
331 # HG changeset patch
331 # HG changeset patch
332 # User test
332 # User test
333 # Date 0 0
333 # Date 0 0
334 # Thu Jan 01 00:00:00 1970 +0000
334 # Thu Jan 01 00:00:00 1970 +0000
335 # Node ID eca050985275bb271ce3092b54e56ea5c85d29a3
335 # Node ID eca050985275bb271ce3092b54e56ea5c85d29a3
336 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
336 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
337 Moved a to foo
337 Moved a to foo
338
338
339 diff --git a/a b/foo
339 diff --git a/a b/foo
340 rename from a
340 rename from a
341 rename to foo
341 rename to foo
342 diff --git a/b b/foobar
342 diff --git a/b b/foobar
343 rename from b
343 rename from b
344 rename to foobar
344 rename to foobar
345
345
346 $ hg mv c wat
346 $ hg mv c wat
347 $ hg unamend
347 $ hg unamend
348
348
349 $ hg verify -v
349 $ hg verify -v
350 repository uses revlog format 1
350 repository uses revlog format 1
351 checking changesets
351 checking changesets
352 checking manifests
352 checking manifests
353 crosschecking files in changesets and manifests
353 crosschecking files in changesets and manifests
354 checking files
354 checking files
355 checked 28 changesets with 16 changes to 11 files
355 checked 28 changesets with 16 changes to 11 files
356
356
357 Retained copies in new prdecessor commit
357 Retained copies in new prdecessor commit
358
358
359 $ hg exp --git
359 $ hg exp --git
360 # HG changeset patch
360 # HG changeset patch
361 # User test
361 # User test
362 # Date 0 0
362 # Date 0 0
363 # Thu Jan 01 00:00:00 1970 +0000
363 # Thu Jan 01 00:00:00 1970 +0000
364 # Node ID 552e3af4f01f620f88ca27be1f898316235b736a
364 # Node ID 552e3af4f01f620f88ca27be1f898316235b736a
365 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
365 # Parent 03ddd6fc5af19e028c44a2fd6d790dd22712f231
366 Moved a to foo
366 Moved a to foo
367
367
368 diff --git a/a b/foo
368 diff --git a/a b/foo
369 rename from a
369 rename from a
370 rename to foo
370 rename to foo
371
371
372 Retained copies in working directoy
372 Retained copies in working directoy
373
373
374 $ hg diff --git
374 $ hg diff --git
375 diff --git a/b b/foobar
375 diff --git a/b b/foobar
376 rename from b
376 rename from b
377 rename to foobar
377 rename to foobar
378 diff --git a/c b/wat
378 diff --git a/c b/wat
379 rename from c
379 rename from c
380 rename to wat
380 rename to wat
381 $ hg revert -qa
381 $ hg revert -qa
382 $ rm foobar wat
382 $ rm foobar wat
383
383
384 Rename a->b, then amend b->c. After unamend, should look like b->c.
384 Rename a->b, then amend b->c. After unamend, should look like b->c.
385
385
386 $ hg co -q 0
386 $ hg co -q 0
387 $ hg mv a b
387 $ hg mv a b
388 $ hg ci -qm 'move to a b'
388 $ hg ci -qm 'move to a b'
389 $ hg mv b c
389 $ hg mv b c
390 $ hg amend
390 $ hg amend
391 $ hg unamend
391 $ hg unamend
392 $ hg st --copies --change .
392 $ hg st --copies --change .
393 A b
393 A b
394 a
394 a
395 R a
395 R a
396 $ hg st --copies
396 $ hg st --copies
397 A c
397 A c
398 b
398 b
399 R b
399 R b
400 $ hg revert -qa
400 $ hg revert -qa
401 $ rm c
401 $ rm c
402
402
403 Rename a->b, then amend b->c, and working copy change c->d. After unamend, should look like b->d
403 Rename a->b, then amend b->c, and working copy change c->d. After unamend, should look like b->d
404
404
405 $ hg co -q 0
405 $ hg co -q 0
406 $ hg mv a b
406 $ hg mv a b
407 $ hg ci -qm 'move to a b'
407 $ hg ci -qm 'move to a b'
408 warning: commit already existed in the repository!
408 $ hg mv b c
409 $ hg mv b c
409 $ hg amend
410 $ hg amend
411 warning: commit already existed in the repository!
410 $ hg mv c d
412 $ hg mv c d
411 $ hg unamend
413 $ hg unamend
412 $ hg st --copies --change .
414 $ hg st --copies --change .
413 A b
415 A b
414 a
416 a
415 R a
417 R a
416 $ hg st --copies
418 $ hg st --copies
417 A d
419 A d
418 b
420 b
419 R b
421 R b
General Comments 0
You need to be logged in to leave comments. Login now