##// END OF EJS Templates
import: add a --secret option...
Denis Laxalde -
r44118:71dbd6f6 default
parent child Browse files
Show More
@@ -1,3982 +1,3984 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 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from .pycompat import (
22 from .pycompat import (
23 getattr,
23 getattr,
24 open,
24 open,
25 setattr,
25 setattr,
26 )
26 )
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 mergeutil,
40 mergeutil,
41 obsolete,
41 obsolete,
42 patch,
42 patch,
43 pathutil,
43 pathutil,
44 phases,
44 phases,
45 pycompat,
45 pycompat,
46 repair,
46 repair,
47 revlog,
47 revlog,
48 rewriteutil,
48 rewriteutil,
49 scmutil,
49 scmutil,
50 smartset,
50 smartset,
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 not globals():
64 if not globals():
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 (b'g', b'git', None, _(b'use git extended diff format')),
172 (b'g', b'git', None, _(b'use git extended diff format')),
173 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
173 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
174 (b'', b'nodates', None, _(b'omit dates from diff headers')),
174 (b'', b'nodates', None, _(b'omit dates from diff headers')),
175 ]
175 ]
176
176
177 diffwsopts = [
177 diffwsopts = [
178 (
178 (
179 b'w',
179 b'w',
180 b'ignore-all-space',
180 b'ignore-all-space',
181 None,
181 None,
182 _(b'ignore white space when comparing lines'),
182 _(b'ignore white space when comparing lines'),
183 ),
183 ),
184 (
184 (
185 b'b',
185 b'b',
186 b'ignore-space-change',
186 b'ignore-space-change',
187 None,
187 None,
188 _(b'ignore changes in the amount of white space'),
188 _(b'ignore changes in the amount of white space'),
189 ),
189 ),
190 (
190 (
191 b'B',
191 b'B',
192 b'ignore-blank-lines',
192 b'ignore-blank-lines',
193 None,
193 None,
194 _(b'ignore changes whose lines are all blank'),
194 _(b'ignore changes whose lines are all blank'),
195 ),
195 ),
196 (
196 (
197 b'Z',
197 b'Z',
198 b'ignore-space-at-eol',
198 b'ignore-space-at-eol',
199 None,
199 None,
200 _(b'ignore changes in whitespace at EOL'),
200 _(b'ignore changes in whitespace at EOL'),
201 ),
201 ),
202 ]
202 ]
203
203
204 diffopts2 = (
204 diffopts2 = (
205 [
205 [
206 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
206 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
207 (
207 (
208 b'p',
208 b'p',
209 b'show-function',
209 b'show-function',
210 None,
210 None,
211 _(b'show which function each change is in'),
211 _(b'show which function each change is in'),
212 ),
212 ),
213 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
213 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
214 ]
214 ]
215 + diffwsopts
215 + diffwsopts
216 + [
216 + [
217 (
217 (
218 b'U',
218 b'U',
219 b'unified',
219 b'unified',
220 b'',
220 b'',
221 _(b'number of lines of context to show'),
221 _(b'number of lines of context to show'),
222 _(b'NUM'),
222 _(b'NUM'),
223 ),
223 ),
224 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
224 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
225 (
225 (
226 b'',
226 b'',
227 b'root',
227 b'root',
228 b'',
228 b'',
229 _(b'produce diffs relative to subdirectory'),
229 _(b'produce diffs relative to subdirectory'),
230 _(b'DIR'),
230 _(b'DIR'),
231 ),
231 ),
232 ]
232 ]
233 )
233 )
234
234
235 mergetoolopts = [
235 mergetoolopts = [
236 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
236 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
237 ]
237 ]
238
238
239 similarityopts = [
239 similarityopts = [
240 (
240 (
241 b's',
241 b's',
242 b'similarity',
242 b'similarity',
243 b'',
243 b'',
244 _(b'guess renamed files by similarity (0<=s<=100)'),
244 _(b'guess renamed files by similarity (0<=s<=100)'),
245 _(b'SIMILARITY'),
245 _(b'SIMILARITY'),
246 )
246 )
247 ]
247 ]
248
248
249 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
249 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
250
250
251 debugrevlogopts = [
251 debugrevlogopts = [
252 (b'c', b'changelog', False, _(b'open changelog')),
252 (b'c', b'changelog', False, _(b'open changelog')),
253 (b'm', b'manifest', False, _(b'open manifest')),
253 (b'm', b'manifest', False, _(b'open manifest')),
254 (b'', b'dir', b'', _(b'open directory manifest')),
254 (b'', b'dir', b'', _(b'open directory manifest')),
255 ]
255 ]
256
256
257 # special string such that everything below this line will be ingored in the
257 # special string such that everything below this line will be ingored in the
258 # editor text
258 # editor text
259 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
259 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
260
260
261
261
262 def resolvecommitoptions(ui, opts):
262 def resolvecommitoptions(ui, opts):
263 """modify commit options dict to handle related options
263 """modify commit options dict to handle related options
264
264
265 The return value indicates that ``rewrite.update-timestamp`` is the reason
265 The return value indicates that ``rewrite.update-timestamp`` is the reason
266 the ``date`` option is set.
266 the ``date`` option is set.
267 """
267 """
268 if opts.get(b'date') and opts.get(b'currentdate'):
268 if opts.get(b'date') and opts.get(b'currentdate'):
269 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
269 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
270 if opts.get(b'user') and opts.get(b'currentuser'):
270 if opts.get(b'user') and opts.get(b'currentuser'):
271 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
271 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
272
272
273 datemaydiffer = False # date-only change should be ignored?
273 datemaydiffer = False # date-only change should be ignored?
274
274
275 if opts.get(b'currentdate'):
275 if opts.get(b'currentdate'):
276 opts[b'date'] = b'%d %d' % dateutil.makedate()
276 opts[b'date'] = b'%d %d' % dateutil.makedate()
277 elif (
277 elif (
278 not opts.get(b'date')
278 not opts.get(b'date')
279 and ui.configbool(b'rewrite', b'update-timestamp')
279 and ui.configbool(b'rewrite', b'update-timestamp')
280 and opts.get(b'currentdate') is None
280 and opts.get(b'currentdate') is None
281 ):
281 ):
282 opts[b'date'] = b'%d %d' % dateutil.makedate()
282 opts[b'date'] = b'%d %d' % dateutil.makedate()
283 datemaydiffer = True
283 datemaydiffer = True
284
284
285 if opts.get(b'currentuser'):
285 if opts.get(b'currentuser'):
286 opts[b'user'] = ui.username()
286 opts[b'user'] = ui.username()
287
287
288 return datemaydiffer
288 return datemaydiffer
289
289
290
290
291 def checknotesize(ui, opts):
291 def checknotesize(ui, opts):
292 """ make sure note is of valid format """
292 """ make sure note is of valid format """
293
293
294 note = opts.get(b'note')
294 note = opts.get(b'note')
295 if not note:
295 if not note:
296 return
296 return
297
297
298 if len(note) > 255:
298 if len(note) > 255:
299 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
299 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
300 if b'\n' in note:
300 if b'\n' in note:
301 raise error.Abort(_(b"note cannot contain a newline"))
301 raise error.Abort(_(b"note cannot contain a newline"))
302
302
303
303
304 def ishunk(x):
304 def ishunk(x):
305 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
305 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
306 return isinstance(x, hunkclasses)
306 return isinstance(x, hunkclasses)
307
307
308
308
309 def newandmodified(chunks, originalchunks):
309 def newandmodified(chunks, originalchunks):
310 newlyaddedandmodifiedfiles = set()
310 newlyaddedandmodifiedfiles = set()
311 alsorestore = set()
311 alsorestore = set()
312 for chunk in chunks:
312 for chunk in chunks:
313 if (
313 if (
314 ishunk(chunk)
314 ishunk(chunk)
315 and chunk.header.isnewfile()
315 and chunk.header.isnewfile()
316 and chunk not in originalchunks
316 and chunk not in originalchunks
317 ):
317 ):
318 newlyaddedandmodifiedfiles.add(chunk.header.filename())
318 newlyaddedandmodifiedfiles.add(chunk.header.filename())
319 alsorestore.update(
319 alsorestore.update(
320 set(chunk.header.files()) - {chunk.header.filename()}
320 set(chunk.header.files()) - {chunk.header.filename()}
321 )
321 )
322 return newlyaddedandmodifiedfiles, alsorestore
322 return newlyaddedandmodifiedfiles, alsorestore
323
323
324
324
325 def parsealiases(cmd):
325 def parsealiases(cmd):
326 return cmd.split(b"|")
326 return cmd.split(b"|")
327
327
328
328
329 def setupwrapcolorwrite(ui):
329 def setupwrapcolorwrite(ui):
330 # wrap ui.write so diff output can be labeled/colorized
330 # wrap ui.write so diff output can be labeled/colorized
331 def wrapwrite(orig, *args, **kw):
331 def wrapwrite(orig, *args, **kw):
332 label = kw.pop('label', b'')
332 label = kw.pop('label', b'')
333 for chunk, l in patch.difflabel(lambda: args):
333 for chunk, l in patch.difflabel(lambda: args):
334 orig(chunk, label=label + l)
334 orig(chunk, label=label + l)
335
335
336 oldwrite = ui.write
336 oldwrite = ui.write
337
337
338 def wrap(*args, **kwargs):
338 def wrap(*args, **kwargs):
339 return wrapwrite(oldwrite, *args, **kwargs)
339 return wrapwrite(oldwrite, *args, **kwargs)
340
340
341 setattr(ui, 'write', wrap)
341 setattr(ui, 'write', wrap)
342 return oldwrite
342 return oldwrite
343
343
344
344
345 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
345 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
346 try:
346 try:
347 if usecurses:
347 if usecurses:
348 if testfile:
348 if testfile:
349 recordfn = crecordmod.testdecorator(
349 recordfn = crecordmod.testdecorator(
350 testfile, crecordmod.testchunkselector
350 testfile, crecordmod.testchunkselector
351 )
351 )
352 else:
352 else:
353 recordfn = crecordmod.chunkselector
353 recordfn = crecordmod.chunkselector
354
354
355 return crecordmod.filterpatch(
355 return crecordmod.filterpatch(
356 ui, originalhunks, recordfn, operation
356 ui, originalhunks, recordfn, operation
357 )
357 )
358 except crecordmod.fallbackerror as e:
358 except crecordmod.fallbackerror as e:
359 ui.warn(b'%s\n' % e.message) # pytype: disable=attribute-error
359 ui.warn(b'%s\n' % e.message) # pytype: disable=attribute-error
360 ui.warn(_(b'falling back to text mode\n'))
360 ui.warn(_(b'falling back to text mode\n'))
361
361
362 return patch.filterpatch(ui, originalhunks, match, operation)
362 return patch.filterpatch(ui, originalhunks, match, operation)
363
363
364
364
365 def recordfilter(ui, originalhunks, match, operation=None):
365 def recordfilter(ui, originalhunks, match, operation=None):
366 """ Prompts the user to filter the originalhunks and return a list of
366 """ Prompts the user to filter the originalhunks and return a list of
367 selected hunks.
367 selected hunks.
368 *operation* is used for to build ui messages to indicate the user what
368 *operation* is used for to build ui messages to indicate the user what
369 kind of filtering they are doing: reverting, committing, shelving, etc.
369 kind of filtering they are doing: reverting, committing, shelving, etc.
370 (see patch.filterpatch).
370 (see patch.filterpatch).
371 """
371 """
372 usecurses = crecordmod.checkcurses(ui)
372 usecurses = crecordmod.checkcurses(ui)
373 testfile = ui.config(b'experimental', b'crecordtest')
373 testfile = ui.config(b'experimental', b'crecordtest')
374 oldwrite = setupwrapcolorwrite(ui)
374 oldwrite = setupwrapcolorwrite(ui)
375 try:
375 try:
376 newchunks, newopts = filterchunks(
376 newchunks, newopts = filterchunks(
377 ui, originalhunks, usecurses, testfile, match, operation
377 ui, originalhunks, usecurses, testfile, match, operation
378 )
378 )
379 finally:
379 finally:
380 ui.write = oldwrite
380 ui.write = oldwrite
381 return newchunks, newopts
381 return newchunks, newopts
382
382
383
383
384 def dorecord(
384 def dorecord(
385 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
385 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
386 ):
386 ):
387 opts = pycompat.byteskwargs(opts)
387 opts = pycompat.byteskwargs(opts)
388 if not ui.interactive():
388 if not ui.interactive():
389 if cmdsuggest:
389 if cmdsuggest:
390 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
390 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
391 else:
391 else:
392 msg = _(b'running non-interactively')
392 msg = _(b'running non-interactively')
393 raise error.Abort(msg)
393 raise error.Abort(msg)
394
394
395 # make sure username is set before going interactive
395 # make sure username is set before going interactive
396 if not opts.get(b'user'):
396 if not opts.get(b'user'):
397 ui.username() # raise exception, username not provided
397 ui.username() # raise exception, username not provided
398
398
399 def recordfunc(ui, repo, message, match, opts):
399 def recordfunc(ui, repo, message, match, opts):
400 """This is generic record driver.
400 """This is generic record driver.
401
401
402 Its job is to interactively filter local changes, and
402 Its job is to interactively filter local changes, and
403 accordingly prepare working directory into a state in which the
403 accordingly prepare working directory into a state in which the
404 job can be delegated to a non-interactive commit command such as
404 job can be delegated to a non-interactive commit command such as
405 'commit' or 'qrefresh'.
405 'commit' or 'qrefresh'.
406
406
407 After the actual job is done by non-interactive command, the
407 After the actual job is done by non-interactive command, the
408 working directory is restored to its original state.
408 working directory is restored to its original state.
409
409
410 In the end we'll record interesting changes, and everything else
410 In the end we'll record interesting changes, and everything else
411 will be left in place, so the user can continue working.
411 will be left in place, so the user can continue working.
412 """
412 """
413 if not opts.get(b'interactive-unshelve'):
413 if not opts.get(b'interactive-unshelve'):
414 checkunfinished(repo, commit=True)
414 checkunfinished(repo, commit=True)
415 wctx = repo[None]
415 wctx = repo[None]
416 merge = len(wctx.parents()) > 1
416 merge = len(wctx.parents()) > 1
417 if merge:
417 if merge:
418 raise error.Abort(
418 raise error.Abort(
419 _(
419 _(
420 b'cannot partially commit a merge '
420 b'cannot partially commit a merge '
421 b'(use "hg commit" instead)'
421 b'(use "hg commit" instead)'
422 )
422 )
423 )
423 )
424
424
425 def fail(f, msg):
425 def fail(f, msg):
426 raise error.Abort(b'%s: %s' % (f, msg))
426 raise error.Abort(b'%s: %s' % (f, msg))
427
427
428 force = opts.get(b'force')
428 force = opts.get(b'force')
429 if not force:
429 if not force:
430 match = matchmod.badmatch(match, fail)
430 match = matchmod.badmatch(match, fail)
431
431
432 status = repo.status(match=match)
432 status = repo.status(match=match)
433
433
434 overrides = {(b'ui', b'commitsubrepos'): True}
434 overrides = {(b'ui', b'commitsubrepos'): True}
435
435
436 with repo.ui.configoverride(overrides, b'record'):
436 with repo.ui.configoverride(overrides, b'record'):
437 # subrepoutil.precommit() modifies the status
437 # subrepoutil.precommit() modifies the status
438 tmpstatus = scmutil.status(
438 tmpstatus = scmutil.status(
439 copymod.copy(status.modified),
439 copymod.copy(status.modified),
440 copymod.copy(status.added),
440 copymod.copy(status.added),
441 copymod.copy(status.removed),
441 copymod.copy(status.removed),
442 copymod.copy(status.deleted),
442 copymod.copy(status.deleted),
443 copymod.copy(status.unknown),
443 copymod.copy(status.unknown),
444 copymod.copy(status.ignored),
444 copymod.copy(status.ignored),
445 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
445 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
446 )
446 )
447
447
448 # Force allows -X subrepo to skip the subrepo.
448 # Force allows -X subrepo to skip the subrepo.
449 subs, commitsubs, newstate = subrepoutil.precommit(
449 subs, commitsubs, newstate = subrepoutil.precommit(
450 repo.ui, wctx, tmpstatus, match, force=True
450 repo.ui, wctx, tmpstatus, match, force=True
451 )
451 )
452 for s in subs:
452 for s in subs:
453 if s in commitsubs:
453 if s in commitsubs:
454 dirtyreason = wctx.sub(s).dirtyreason(True)
454 dirtyreason = wctx.sub(s).dirtyreason(True)
455 raise error.Abort(dirtyreason)
455 raise error.Abort(dirtyreason)
456
456
457 if not force:
457 if not force:
458 repo.checkcommitpatterns(wctx, match, status, fail)
458 repo.checkcommitpatterns(wctx, match, status, fail)
459 diffopts = patch.difffeatureopts(
459 diffopts = patch.difffeatureopts(
460 ui,
460 ui,
461 opts=opts,
461 opts=opts,
462 whitespace=True,
462 whitespace=True,
463 section=b'commands',
463 section=b'commands',
464 configprefix=b'commit.interactive.',
464 configprefix=b'commit.interactive.',
465 )
465 )
466 diffopts.nodates = True
466 diffopts.nodates = True
467 diffopts.git = True
467 diffopts.git = True
468 diffopts.showfunc = True
468 diffopts.showfunc = True
469 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
469 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
470 originalchunks = patch.parsepatch(originaldiff)
470 originalchunks = patch.parsepatch(originaldiff)
471 match = scmutil.match(repo[None], pats)
471 match = scmutil.match(repo[None], pats)
472
472
473 # 1. filter patch, since we are intending to apply subset of it
473 # 1. filter patch, since we are intending to apply subset of it
474 try:
474 try:
475 chunks, newopts = filterfn(ui, originalchunks, match)
475 chunks, newopts = filterfn(ui, originalchunks, match)
476 except error.PatchError as err:
476 except error.PatchError as err:
477 raise error.Abort(_(b'error parsing patch: %s') % err)
477 raise error.Abort(_(b'error parsing patch: %s') % err)
478 opts.update(newopts)
478 opts.update(newopts)
479
479
480 # We need to keep a backup of files that have been newly added and
480 # We need to keep a backup of files that have been newly added and
481 # modified during the recording process because there is a previous
481 # modified during the recording process because there is a previous
482 # version without the edit in the workdir. We also will need to restore
482 # version without the edit in the workdir. We also will need to restore
483 # files that were the sources of renames so that the patch application
483 # files that were the sources of renames so that the patch application
484 # works.
484 # works.
485 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
485 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
486 chunks, originalchunks
486 chunks, originalchunks
487 )
487 )
488 contenders = set()
488 contenders = set()
489 for h in chunks:
489 for h in chunks:
490 try:
490 try:
491 contenders.update(set(h.files()))
491 contenders.update(set(h.files()))
492 except AttributeError:
492 except AttributeError:
493 pass
493 pass
494
494
495 changed = status.modified + status.added + status.removed
495 changed = status.modified + status.added + status.removed
496 newfiles = [f for f in changed if f in contenders]
496 newfiles = [f for f in changed if f in contenders]
497 if not newfiles:
497 if not newfiles:
498 ui.status(_(b'no changes to record\n'))
498 ui.status(_(b'no changes to record\n'))
499 return 0
499 return 0
500
500
501 modified = set(status.modified)
501 modified = set(status.modified)
502
502
503 # 2. backup changed files, so we can restore them in the end
503 # 2. backup changed files, so we can restore them in the end
504
504
505 if backupall:
505 if backupall:
506 tobackup = changed
506 tobackup = changed
507 else:
507 else:
508 tobackup = [
508 tobackup = [
509 f
509 f
510 for f in newfiles
510 for f in newfiles
511 if f in modified or f in newlyaddedandmodifiedfiles
511 if f in modified or f in newlyaddedandmodifiedfiles
512 ]
512 ]
513 backups = {}
513 backups = {}
514 if tobackup:
514 if tobackup:
515 backupdir = repo.vfs.join(b'record-backups')
515 backupdir = repo.vfs.join(b'record-backups')
516 try:
516 try:
517 os.mkdir(backupdir)
517 os.mkdir(backupdir)
518 except OSError as err:
518 except OSError as err:
519 if err.errno != errno.EEXIST:
519 if err.errno != errno.EEXIST:
520 raise
520 raise
521 try:
521 try:
522 # backup continues
522 # backup continues
523 for f in tobackup:
523 for f in tobackup:
524 fd, tmpname = pycompat.mkstemp(
524 fd, tmpname = pycompat.mkstemp(
525 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
525 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
526 )
526 )
527 os.close(fd)
527 os.close(fd)
528 ui.debug(b'backup %r as %r\n' % (f, tmpname))
528 ui.debug(b'backup %r as %r\n' % (f, tmpname))
529 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
529 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
530 backups[f] = tmpname
530 backups[f] = tmpname
531
531
532 fp = stringio()
532 fp = stringio()
533 for c in chunks:
533 for c in chunks:
534 fname = c.filename()
534 fname = c.filename()
535 if fname in backups:
535 if fname in backups:
536 c.write(fp)
536 c.write(fp)
537 dopatch = fp.tell()
537 dopatch = fp.tell()
538 fp.seek(0)
538 fp.seek(0)
539
539
540 # 2.5 optionally review / modify patch in text editor
540 # 2.5 optionally review / modify patch in text editor
541 if opts.get(b'review', False):
541 if opts.get(b'review', False):
542 patchtext = (
542 patchtext = (
543 crecordmod.diffhelptext
543 crecordmod.diffhelptext
544 + crecordmod.patchhelptext
544 + crecordmod.patchhelptext
545 + fp.read()
545 + fp.read()
546 )
546 )
547 reviewedpatch = ui.edit(
547 reviewedpatch = ui.edit(
548 patchtext, b"", action=b"diff", repopath=repo.path
548 patchtext, b"", action=b"diff", repopath=repo.path
549 )
549 )
550 fp.truncate(0)
550 fp.truncate(0)
551 fp.write(reviewedpatch)
551 fp.write(reviewedpatch)
552 fp.seek(0)
552 fp.seek(0)
553
553
554 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
554 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
555 # 3a. apply filtered patch to clean repo (clean)
555 # 3a. apply filtered patch to clean repo (clean)
556 if backups:
556 if backups:
557 # Equivalent to hg.revert
557 # Equivalent to hg.revert
558 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
558 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
559 mergemod.update(
559 mergemod.update(
560 repo,
560 repo,
561 repo.dirstate.p1(),
561 repo.dirstate.p1(),
562 branchmerge=False,
562 branchmerge=False,
563 force=True,
563 force=True,
564 matcher=m,
564 matcher=m,
565 )
565 )
566
566
567 # 3b. (apply)
567 # 3b. (apply)
568 if dopatch:
568 if dopatch:
569 try:
569 try:
570 ui.debug(b'applying patch\n')
570 ui.debug(b'applying patch\n')
571 ui.debug(fp.getvalue())
571 ui.debug(fp.getvalue())
572 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
572 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
573 except error.PatchError as err:
573 except error.PatchError as err:
574 raise error.Abort(pycompat.bytestr(err))
574 raise error.Abort(pycompat.bytestr(err))
575 del fp
575 del fp
576
576
577 # 4. We prepared working directory according to filtered
577 # 4. We prepared working directory according to filtered
578 # patch. Now is the time to delegate the job to
578 # patch. Now is the time to delegate the job to
579 # commit/qrefresh or the like!
579 # commit/qrefresh or the like!
580
580
581 # Make all of the pathnames absolute.
581 # Make all of the pathnames absolute.
582 newfiles = [repo.wjoin(nf) for nf in newfiles]
582 newfiles = [repo.wjoin(nf) for nf in newfiles]
583 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
583 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
584 finally:
584 finally:
585 # 5. finally restore backed-up files
585 # 5. finally restore backed-up files
586 try:
586 try:
587 dirstate = repo.dirstate
587 dirstate = repo.dirstate
588 for realname, tmpname in pycompat.iteritems(backups):
588 for realname, tmpname in pycompat.iteritems(backups):
589 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
589 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
590
590
591 if dirstate[realname] == b'n':
591 if dirstate[realname] == b'n':
592 # without normallookup, restoring timestamp
592 # without normallookup, restoring timestamp
593 # may cause partially committed files
593 # may cause partially committed files
594 # to be treated as unmodified
594 # to be treated as unmodified
595 dirstate.normallookup(realname)
595 dirstate.normallookup(realname)
596
596
597 # copystat=True here and above are a hack to trick any
597 # copystat=True here and above are a hack to trick any
598 # editors that have f open that we haven't modified them.
598 # editors that have f open that we haven't modified them.
599 #
599 #
600 # Also note that this racy as an editor could notice the
600 # Also note that this racy as an editor could notice the
601 # file's mtime before we've finished writing it.
601 # file's mtime before we've finished writing it.
602 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
602 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
603 os.unlink(tmpname)
603 os.unlink(tmpname)
604 if tobackup:
604 if tobackup:
605 os.rmdir(backupdir)
605 os.rmdir(backupdir)
606 except OSError:
606 except OSError:
607 pass
607 pass
608
608
609 def recordinwlock(ui, repo, message, match, opts):
609 def recordinwlock(ui, repo, message, match, opts):
610 with repo.wlock():
610 with repo.wlock():
611 return recordfunc(ui, repo, message, match, opts)
611 return recordfunc(ui, repo, message, match, opts)
612
612
613 return commit(ui, repo, recordinwlock, pats, opts)
613 return commit(ui, repo, recordinwlock, pats, opts)
614
614
615
615
616 class dirnode(object):
616 class dirnode(object):
617 """
617 """
618 Represent a directory in user working copy with information required for
618 Represent a directory in user working copy with information required for
619 the purpose of tersing its status.
619 the purpose of tersing its status.
620
620
621 path is the path to the directory, without a trailing '/'
621 path is the path to the directory, without a trailing '/'
622
622
623 statuses is a set of statuses of all files in this directory (this includes
623 statuses is a set of statuses of all files in this directory (this includes
624 all the files in all the subdirectories too)
624 all the files in all the subdirectories too)
625
625
626 files is a list of files which are direct child of this directory
626 files is a list of files which are direct child of this directory
627
627
628 subdirs is a dictionary of sub-directory name as the key and it's own
628 subdirs is a dictionary of sub-directory name as the key and it's own
629 dirnode object as the value
629 dirnode object as the value
630 """
630 """
631
631
632 def __init__(self, dirpath):
632 def __init__(self, dirpath):
633 self.path = dirpath
633 self.path = dirpath
634 self.statuses = set()
634 self.statuses = set()
635 self.files = []
635 self.files = []
636 self.subdirs = {}
636 self.subdirs = {}
637
637
638 def _addfileindir(self, filename, status):
638 def _addfileindir(self, filename, status):
639 """Add a file in this directory as a direct child."""
639 """Add a file in this directory as a direct child."""
640 self.files.append((filename, status))
640 self.files.append((filename, status))
641
641
642 def addfile(self, filename, status):
642 def addfile(self, filename, status):
643 """
643 """
644 Add a file to this directory or to its direct parent directory.
644 Add a file to this directory or to its direct parent directory.
645
645
646 If the file is not direct child of this directory, we traverse to the
646 If the file is not direct child of this directory, we traverse to the
647 directory of which this file is a direct child of and add the file
647 directory of which this file is a direct child of and add the file
648 there.
648 there.
649 """
649 """
650
650
651 # the filename contains a path separator, it means it's not the direct
651 # the filename contains a path separator, it means it's not the direct
652 # child of this directory
652 # child of this directory
653 if b'/' in filename:
653 if b'/' in filename:
654 subdir, filep = filename.split(b'/', 1)
654 subdir, filep = filename.split(b'/', 1)
655
655
656 # does the dirnode object for subdir exists
656 # does the dirnode object for subdir exists
657 if subdir not in self.subdirs:
657 if subdir not in self.subdirs:
658 subdirpath = pathutil.join(self.path, subdir)
658 subdirpath = pathutil.join(self.path, subdir)
659 self.subdirs[subdir] = dirnode(subdirpath)
659 self.subdirs[subdir] = dirnode(subdirpath)
660
660
661 # try adding the file in subdir
661 # try adding the file in subdir
662 self.subdirs[subdir].addfile(filep, status)
662 self.subdirs[subdir].addfile(filep, status)
663
663
664 else:
664 else:
665 self._addfileindir(filename, status)
665 self._addfileindir(filename, status)
666
666
667 if status not in self.statuses:
667 if status not in self.statuses:
668 self.statuses.add(status)
668 self.statuses.add(status)
669
669
670 def iterfilepaths(self):
670 def iterfilepaths(self):
671 """Yield (status, path) for files directly under this directory."""
671 """Yield (status, path) for files directly under this directory."""
672 for f, st in self.files:
672 for f, st in self.files:
673 yield st, pathutil.join(self.path, f)
673 yield st, pathutil.join(self.path, f)
674
674
675 def tersewalk(self, terseargs):
675 def tersewalk(self, terseargs):
676 """
676 """
677 Yield (status, path) obtained by processing the status of this
677 Yield (status, path) obtained by processing the status of this
678 dirnode.
678 dirnode.
679
679
680 terseargs is the string of arguments passed by the user with `--terse`
680 terseargs is the string of arguments passed by the user with `--terse`
681 flag.
681 flag.
682
682
683 Following are the cases which can happen:
683 Following are the cases which can happen:
684
684
685 1) All the files in the directory (including all the files in its
685 1) All the files in the directory (including all the files in its
686 subdirectories) share the same status and the user has asked us to terse
686 subdirectories) share the same status and the user has asked us to terse
687 that status. -> yield (status, dirpath). dirpath will end in '/'.
687 that status. -> yield (status, dirpath). dirpath will end in '/'.
688
688
689 2) Otherwise, we do following:
689 2) Otherwise, we do following:
690
690
691 a) Yield (status, filepath) for all the files which are in this
691 a) Yield (status, filepath) for all the files which are in this
692 directory (only the ones in this directory, not the subdirs)
692 directory (only the ones in this directory, not the subdirs)
693
693
694 b) Recurse the function on all the subdirectories of this
694 b) Recurse the function on all the subdirectories of this
695 directory
695 directory
696 """
696 """
697
697
698 if len(self.statuses) == 1:
698 if len(self.statuses) == 1:
699 onlyst = self.statuses.pop()
699 onlyst = self.statuses.pop()
700
700
701 # Making sure we terse only when the status abbreviation is
701 # Making sure we terse only when the status abbreviation is
702 # passed as terse argument
702 # passed as terse argument
703 if onlyst in terseargs:
703 if onlyst in terseargs:
704 yield onlyst, self.path + b'/'
704 yield onlyst, self.path + b'/'
705 return
705 return
706
706
707 # add the files to status list
707 # add the files to status list
708 for st, fpath in self.iterfilepaths():
708 for st, fpath in self.iterfilepaths():
709 yield st, fpath
709 yield st, fpath
710
710
711 # recurse on the subdirs
711 # recurse on the subdirs
712 for dirobj in self.subdirs.values():
712 for dirobj in self.subdirs.values():
713 for st, fpath in dirobj.tersewalk(terseargs):
713 for st, fpath in dirobj.tersewalk(terseargs):
714 yield st, fpath
714 yield st, fpath
715
715
716
716
717 def tersedir(statuslist, terseargs):
717 def tersedir(statuslist, terseargs):
718 """
718 """
719 Terse the status if all the files in a directory shares the same status.
719 Terse the status if all the files in a directory shares the same status.
720
720
721 statuslist is scmutil.status() object which contains a list of files for
721 statuslist is scmutil.status() object which contains a list of files for
722 each status.
722 each status.
723 terseargs is string which is passed by the user as the argument to `--terse`
723 terseargs is string which is passed by the user as the argument to `--terse`
724 flag.
724 flag.
725
725
726 The function makes a tree of objects of dirnode class, and at each node it
726 The function makes a tree of objects of dirnode class, and at each node it
727 stores the information required to know whether we can terse a certain
727 stores the information required to know whether we can terse a certain
728 directory or not.
728 directory or not.
729 """
729 """
730 # the order matters here as that is used to produce final list
730 # the order matters here as that is used to produce final list
731 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
731 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
732
732
733 # checking the argument validity
733 # checking the argument validity
734 for s in pycompat.bytestr(terseargs):
734 for s in pycompat.bytestr(terseargs):
735 if s not in allst:
735 if s not in allst:
736 raise error.Abort(_(b"'%s' not recognized") % s)
736 raise error.Abort(_(b"'%s' not recognized") % s)
737
737
738 # creating a dirnode object for the root of the repo
738 # creating a dirnode object for the root of the repo
739 rootobj = dirnode(b'')
739 rootobj = dirnode(b'')
740 pstatus = (
740 pstatus = (
741 b'modified',
741 b'modified',
742 b'added',
742 b'added',
743 b'deleted',
743 b'deleted',
744 b'clean',
744 b'clean',
745 b'unknown',
745 b'unknown',
746 b'ignored',
746 b'ignored',
747 b'removed',
747 b'removed',
748 )
748 )
749
749
750 tersedict = {}
750 tersedict = {}
751 for attrname in pstatus:
751 for attrname in pstatus:
752 statuschar = attrname[0:1]
752 statuschar = attrname[0:1]
753 for f in getattr(statuslist, attrname):
753 for f in getattr(statuslist, attrname):
754 rootobj.addfile(f, statuschar)
754 rootobj.addfile(f, statuschar)
755 tersedict[statuschar] = []
755 tersedict[statuschar] = []
756
756
757 # we won't be tersing the root dir, so add files in it
757 # we won't be tersing the root dir, so add files in it
758 for st, fpath in rootobj.iterfilepaths():
758 for st, fpath in rootobj.iterfilepaths():
759 tersedict[st].append(fpath)
759 tersedict[st].append(fpath)
760
760
761 # process each sub-directory and build tersedict
761 # process each sub-directory and build tersedict
762 for subdir in rootobj.subdirs.values():
762 for subdir in rootobj.subdirs.values():
763 for st, f in subdir.tersewalk(terseargs):
763 for st, f in subdir.tersewalk(terseargs):
764 tersedict[st].append(f)
764 tersedict[st].append(f)
765
765
766 tersedlist = []
766 tersedlist = []
767 for st in allst:
767 for st in allst:
768 tersedict[st].sort()
768 tersedict[st].sort()
769 tersedlist.append(tersedict[st])
769 tersedlist.append(tersedict[st])
770
770
771 return scmutil.status(*tersedlist)
771 return scmutil.status(*tersedlist)
772
772
773
773
774 def _commentlines(raw):
774 def _commentlines(raw):
775 '''Surround lineswith a comment char and a new line'''
775 '''Surround lineswith a comment char and a new line'''
776 lines = raw.splitlines()
776 lines = raw.splitlines()
777 commentedlines = [b'# %s' % line for line in lines]
777 commentedlines = [b'# %s' % line for line in lines]
778 return b'\n'.join(commentedlines) + b'\n'
778 return b'\n'.join(commentedlines) + b'\n'
779
779
780
780
781 def _conflictsmsg(repo):
781 def _conflictsmsg(repo):
782 mergestate = mergemod.mergestate.read(repo)
782 mergestate = mergemod.mergestate.read(repo)
783 if not mergestate.active():
783 if not mergestate.active():
784 return
784 return
785
785
786 m = scmutil.match(repo[None])
786 m = scmutil.match(repo[None])
787 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
787 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
788 if unresolvedlist:
788 if unresolvedlist:
789 mergeliststr = b'\n'.join(
789 mergeliststr = b'\n'.join(
790 [
790 [
791 b' %s' % util.pathto(repo.root, encoding.getcwd(), path)
791 b' %s' % util.pathto(repo.root, encoding.getcwd(), path)
792 for path in sorted(unresolvedlist)
792 for path in sorted(unresolvedlist)
793 ]
793 ]
794 )
794 )
795 msg = (
795 msg = (
796 _(
796 _(
797 '''Unresolved merge conflicts:
797 '''Unresolved merge conflicts:
798
798
799 %s
799 %s
800
800
801 To mark files as resolved: hg resolve --mark FILE'''
801 To mark files as resolved: hg resolve --mark FILE'''
802 )
802 )
803 % mergeliststr
803 % mergeliststr
804 )
804 )
805 else:
805 else:
806 msg = _(b'No unresolved merge conflicts.')
806 msg = _(b'No unresolved merge conflicts.')
807
807
808 return _commentlines(msg)
808 return _commentlines(msg)
809
809
810
810
811 def morestatus(repo, fm):
811 def morestatus(repo, fm):
812 statetuple = statemod.getrepostate(repo)
812 statetuple = statemod.getrepostate(repo)
813 label = b'status.morestatus'
813 label = b'status.morestatus'
814 if statetuple:
814 if statetuple:
815 state, helpfulmsg = statetuple
815 state, helpfulmsg = statetuple
816 statemsg = _(b'The repository is in an unfinished *%s* state.') % state
816 statemsg = _(b'The repository is in an unfinished *%s* state.') % state
817 fm.plain(b'%s\n' % _commentlines(statemsg), label=label)
817 fm.plain(b'%s\n' % _commentlines(statemsg), label=label)
818 conmsg = _conflictsmsg(repo)
818 conmsg = _conflictsmsg(repo)
819 if conmsg:
819 if conmsg:
820 fm.plain(b'%s\n' % conmsg, label=label)
820 fm.plain(b'%s\n' % conmsg, label=label)
821 if helpfulmsg:
821 if helpfulmsg:
822 fm.plain(b'%s\n' % _commentlines(helpfulmsg), label=label)
822 fm.plain(b'%s\n' % _commentlines(helpfulmsg), label=label)
823
823
824
824
825 def findpossible(cmd, table, strict=False):
825 def findpossible(cmd, table, strict=False):
826 """
826 """
827 Return cmd -> (aliases, command table entry)
827 Return cmd -> (aliases, command table entry)
828 for each matching command.
828 for each matching command.
829 Return debug commands (or their aliases) only if no normal command matches.
829 Return debug commands (or their aliases) only if no normal command matches.
830 """
830 """
831 choice = {}
831 choice = {}
832 debugchoice = {}
832 debugchoice = {}
833
833
834 if cmd in table:
834 if cmd in table:
835 # short-circuit exact matches, "log" alias beats "log|history"
835 # short-circuit exact matches, "log" alias beats "log|history"
836 keys = [cmd]
836 keys = [cmd]
837 else:
837 else:
838 keys = table.keys()
838 keys = table.keys()
839
839
840 allcmds = []
840 allcmds = []
841 for e in keys:
841 for e in keys:
842 aliases = parsealiases(e)
842 aliases = parsealiases(e)
843 allcmds.extend(aliases)
843 allcmds.extend(aliases)
844 found = None
844 found = None
845 if cmd in aliases:
845 if cmd in aliases:
846 found = cmd
846 found = cmd
847 elif not strict:
847 elif not strict:
848 for a in aliases:
848 for a in aliases:
849 if a.startswith(cmd):
849 if a.startswith(cmd):
850 found = a
850 found = a
851 break
851 break
852 if found is not None:
852 if found is not None:
853 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
853 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
854 debugchoice[found] = (aliases, table[e])
854 debugchoice[found] = (aliases, table[e])
855 else:
855 else:
856 choice[found] = (aliases, table[e])
856 choice[found] = (aliases, table[e])
857
857
858 if not choice and debugchoice:
858 if not choice and debugchoice:
859 choice = debugchoice
859 choice = debugchoice
860
860
861 return choice, allcmds
861 return choice, allcmds
862
862
863
863
864 def findcmd(cmd, table, strict=True):
864 def findcmd(cmd, table, strict=True):
865 """Return (aliases, command table entry) for command string."""
865 """Return (aliases, command table entry) for command string."""
866 choice, allcmds = findpossible(cmd, table, strict)
866 choice, allcmds = findpossible(cmd, table, strict)
867
867
868 if cmd in choice:
868 if cmd in choice:
869 return choice[cmd]
869 return choice[cmd]
870
870
871 if len(choice) > 1:
871 if len(choice) > 1:
872 clist = sorted(choice)
872 clist = sorted(choice)
873 raise error.AmbiguousCommand(cmd, clist)
873 raise error.AmbiguousCommand(cmd, clist)
874
874
875 if choice:
875 if choice:
876 return list(choice.values())[0]
876 return list(choice.values())[0]
877
877
878 raise error.UnknownCommand(cmd, allcmds)
878 raise error.UnknownCommand(cmd, allcmds)
879
879
880
880
881 def changebranch(ui, repo, revs, label):
881 def changebranch(ui, repo, revs, label):
882 """ Change the branch name of given revs to label """
882 """ Change the branch name of given revs to label """
883
883
884 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
884 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
885 # abort in case of uncommitted merge or dirty wdir
885 # abort in case of uncommitted merge or dirty wdir
886 bailifchanged(repo)
886 bailifchanged(repo)
887 revs = scmutil.revrange(repo, revs)
887 revs = scmutil.revrange(repo, revs)
888 if not revs:
888 if not revs:
889 raise error.Abort(b"empty revision set")
889 raise error.Abort(b"empty revision set")
890 roots = repo.revs(b'roots(%ld)', revs)
890 roots = repo.revs(b'roots(%ld)', revs)
891 if len(roots) > 1:
891 if len(roots) > 1:
892 raise error.Abort(
892 raise error.Abort(
893 _(b"cannot change branch of non-linear revisions")
893 _(b"cannot change branch of non-linear revisions")
894 )
894 )
895 rewriteutil.precheck(repo, revs, b'change branch of')
895 rewriteutil.precheck(repo, revs, b'change branch of')
896
896
897 root = repo[roots.first()]
897 root = repo[roots.first()]
898 rpb = {parent.branch() for parent in root.parents()}
898 rpb = {parent.branch() for parent in root.parents()}
899 if label not in rpb and label in repo.branchmap():
899 if label not in rpb and label in repo.branchmap():
900 raise error.Abort(_(b"a branch of the same name already exists"))
900 raise error.Abort(_(b"a branch of the same name already exists"))
901
901
902 if repo.revs(b'obsolete() and %ld', revs):
902 if repo.revs(b'obsolete() and %ld', revs):
903 raise error.Abort(
903 raise error.Abort(
904 _(b"cannot change branch of a obsolete changeset")
904 _(b"cannot change branch of a obsolete changeset")
905 )
905 )
906
906
907 # make sure only topological heads
907 # make sure only topological heads
908 if repo.revs(b'heads(%ld) - head()', revs):
908 if repo.revs(b'heads(%ld) - head()', revs):
909 raise error.Abort(_(b"cannot change branch in middle of a stack"))
909 raise error.Abort(_(b"cannot change branch in middle of a stack"))
910
910
911 replacements = {}
911 replacements = {}
912 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
912 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
913 # mercurial.subrepo -> mercurial.cmdutil
913 # mercurial.subrepo -> mercurial.cmdutil
914 from . import context
914 from . import context
915
915
916 for rev in revs:
916 for rev in revs:
917 ctx = repo[rev]
917 ctx = repo[rev]
918 oldbranch = ctx.branch()
918 oldbranch = ctx.branch()
919 # check if ctx has same branch
919 # check if ctx has same branch
920 if oldbranch == label:
920 if oldbranch == label:
921 continue
921 continue
922
922
923 def filectxfn(repo, newctx, path):
923 def filectxfn(repo, newctx, path):
924 try:
924 try:
925 return ctx[path]
925 return ctx[path]
926 except error.ManifestLookupError:
926 except error.ManifestLookupError:
927 return None
927 return None
928
928
929 ui.debug(
929 ui.debug(
930 b"changing branch of '%s' from '%s' to '%s'\n"
930 b"changing branch of '%s' from '%s' to '%s'\n"
931 % (hex(ctx.node()), oldbranch, label)
931 % (hex(ctx.node()), oldbranch, label)
932 )
932 )
933 extra = ctx.extra()
933 extra = ctx.extra()
934 extra[b'branch_change'] = hex(ctx.node())
934 extra[b'branch_change'] = hex(ctx.node())
935 # While changing branch of set of linear commits, make sure that
935 # While changing branch of set of linear commits, make sure that
936 # we base our commits on new parent rather than old parent which
936 # we base our commits on new parent rather than old parent which
937 # was obsoleted while changing the branch
937 # was obsoleted while changing the branch
938 p1 = ctx.p1().node()
938 p1 = ctx.p1().node()
939 p2 = ctx.p2().node()
939 p2 = ctx.p2().node()
940 if p1 in replacements:
940 if p1 in replacements:
941 p1 = replacements[p1][0]
941 p1 = replacements[p1][0]
942 if p2 in replacements:
942 if p2 in replacements:
943 p2 = replacements[p2][0]
943 p2 = replacements[p2][0]
944
944
945 mc = context.memctx(
945 mc = context.memctx(
946 repo,
946 repo,
947 (p1, p2),
947 (p1, p2),
948 ctx.description(),
948 ctx.description(),
949 ctx.files(),
949 ctx.files(),
950 filectxfn,
950 filectxfn,
951 user=ctx.user(),
951 user=ctx.user(),
952 date=ctx.date(),
952 date=ctx.date(),
953 extra=extra,
953 extra=extra,
954 branch=label,
954 branch=label,
955 )
955 )
956
956
957 newnode = repo.commitctx(mc)
957 newnode = repo.commitctx(mc)
958 replacements[ctx.node()] = (newnode,)
958 replacements[ctx.node()] = (newnode,)
959 ui.debug(b'new node id is %s\n' % hex(newnode))
959 ui.debug(b'new node id is %s\n' % hex(newnode))
960
960
961 # create obsmarkers and move bookmarks
961 # create obsmarkers and move bookmarks
962 scmutil.cleanupnodes(
962 scmutil.cleanupnodes(
963 repo, replacements, b'branch-change', fixphase=True
963 repo, replacements, b'branch-change', fixphase=True
964 )
964 )
965
965
966 # move the working copy too
966 # move the working copy too
967 wctx = repo[None]
967 wctx = repo[None]
968 # in-progress merge is a bit too complex for now.
968 # in-progress merge is a bit too complex for now.
969 if len(wctx.parents()) == 1:
969 if len(wctx.parents()) == 1:
970 newid = replacements.get(wctx.p1().node())
970 newid = replacements.get(wctx.p1().node())
971 if newid is not None:
971 if newid is not None:
972 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
972 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
973 # mercurial.cmdutil
973 # mercurial.cmdutil
974 from . import hg
974 from . import hg
975
975
976 hg.update(repo, newid[0], quietempty=True)
976 hg.update(repo, newid[0], quietempty=True)
977
977
978 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
978 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
979
979
980
980
981 def findrepo(p):
981 def findrepo(p):
982 while not os.path.isdir(os.path.join(p, b".hg")):
982 while not os.path.isdir(os.path.join(p, b".hg")):
983 oldp, p = p, os.path.dirname(p)
983 oldp, p = p, os.path.dirname(p)
984 if p == oldp:
984 if p == oldp:
985 return None
985 return None
986
986
987 return p
987 return p
988
988
989
989
990 def bailifchanged(repo, merge=True, hint=None):
990 def bailifchanged(repo, merge=True, hint=None):
991 """ enforce the precondition that working directory must be clean.
991 """ enforce the precondition that working directory must be clean.
992
992
993 'merge' can be set to false if a pending uncommitted merge should be
993 'merge' can be set to false if a pending uncommitted merge should be
994 ignored (such as when 'update --check' runs).
994 ignored (such as when 'update --check' runs).
995
995
996 'hint' is the usual hint given to Abort exception.
996 'hint' is the usual hint given to Abort exception.
997 """
997 """
998
998
999 if merge and repo.dirstate.p2() != nullid:
999 if merge and repo.dirstate.p2() != nullid:
1000 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1000 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1001 st = repo.status()
1001 st = repo.status()
1002 if st.modified or st.added or st.removed or st.deleted:
1002 if st.modified or st.added or st.removed or st.deleted:
1003 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1003 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1004 ctx = repo[None]
1004 ctx = repo[None]
1005 for s in sorted(ctx.substate):
1005 for s in sorted(ctx.substate):
1006 ctx.sub(s).bailifchanged(hint=hint)
1006 ctx.sub(s).bailifchanged(hint=hint)
1007
1007
1008
1008
1009 def logmessage(ui, opts):
1009 def logmessage(ui, opts):
1010 """ get the log message according to -m and -l option """
1010 """ get the log message according to -m and -l option """
1011 message = opts.get(b'message')
1011 message = opts.get(b'message')
1012 logfile = opts.get(b'logfile')
1012 logfile = opts.get(b'logfile')
1013
1013
1014 if message and logfile:
1014 if message and logfile:
1015 raise error.Abort(
1015 raise error.Abort(
1016 _(b'options --message and --logfile are mutually exclusive')
1016 _(b'options --message and --logfile are mutually exclusive')
1017 )
1017 )
1018 if not message and logfile:
1018 if not message and logfile:
1019 try:
1019 try:
1020 if isstdiofilename(logfile):
1020 if isstdiofilename(logfile):
1021 message = ui.fin.read()
1021 message = ui.fin.read()
1022 else:
1022 else:
1023 message = b'\n'.join(util.readfile(logfile).splitlines())
1023 message = b'\n'.join(util.readfile(logfile).splitlines())
1024 except IOError as inst:
1024 except IOError as inst:
1025 raise error.Abort(
1025 raise error.Abort(
1026 _(b"can't read commit message '%s': %s")
1026 _(b"can't read commit message '%s': %s")
1027 % (logfile, encoding.strtolocal(inst.strerror))
1027 % (logfile, encoding.strtolocal(inst.strerror))
1028 )
1028 )
1029 return message
1029 return message
1030
1030
1031
1031
1032 def mergeeditform(ctxorbool, baseformname):
1032 def mergeeditform(ctxorbool, baseformname):
1033 """return appropriate editform name (referencing a committemplate)
1033 """return appropriate editform name (referencing a committemplate)
1034
1034
1035 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1035 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1036 merging is committed.
1036 merging is committed.
1037
1037
1038 This returns baseformname with '.merge' appended if it is a merge,
1038 This returns baseformname with '.merge' appended if it is a merge,
1039 otherwise '.normal' is appended.
1039 otherwise '.normal' is appended.
1040 """
1040 """
1041 if isinstance(ctxorbool, bool):
1041 if isinstance(ctxorbool, bool):
1042 if ctxorbool:
1042 if ctxorbool:
1043 return baseformname + b".merge"
1043 return baseformname + b".merge"
1044 elif len(ctxorbool.parents()) > 1:
1044 elif len(ctxorbool.parents()) > 1:
1045 return baseformname + b".merge"
1045 return baseformname + b".merge"
1046
1046
1047 return baseformname + b".normal"
1047 return baseformname + b".normal"
1048
1048
1049
1049
1050 def getcommiteditor(
1050 def getcommiteditor(
1051 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1051 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1052 ):
1052 ):
1053 """get appropriate commit message editor according to '--edit' option
1053 """get appropriate commit message editor according to '--edit' option
1054
1054
1055 'finishdesc' is a function to be called with edited commit message
1055 'finishdesc' is a function to be called with edited commit message
1056 (= 'description' of the new changeset) just after editing, but
1056 (= 'description' of the new changeset) just after editing, but
1057 before checking empty-ness. It should return actual text to be
1057 before checking empty-ness. It should return actual text to be
1058 stored into history. This allows to change description before
1058 stored into history. This allows to change description before
1059 storing.
1059 storing.
1060
1060
1061 'extramsg' is a extra message to be shown in the editor instead of
1061 'extramsg' is a extra message to be shown in the editor instead of
1062 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1062 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1063 is automatically added.
1063 is automatically added.
1064
1064
1065 'editform' is a dot-separated list of names, to distinguish
1065 'editform' is a dot-separated list of names, to distinguish
1066 the purpose of commit text editing.
1066 the purpose of commit text editing.
1067
1067
1068 'getcommiteditor' returns 'commitforceeditor' regardless of
1068 'getcommiteditor' returns 'commitforceeditor' regardless of
1069 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1069 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1070 they are specific for usage in MQ.
1070 they are specific for usage in MQ.
1071 """
1071 """
1072 if edit or finishdesc or extramsg:
1072 if edit or finishdesc or extramsg:
1073 return lambda r, c, s: commitforceeditor(
1073 return lambda r, c, s: commitforceeditor(
1074 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1074 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1075 )
1075 )
1076 elif editform:
1076 elif editform:
1077 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1077 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1078 else:
1078 else:
1079 return commiteditor
1079 return commiteditor
1080
1080
1081
1081
1082 def _escapecommandtemplate(tmpl):
1082 def _escapecommandtemplate(tmpl):
1083 parts = []
1083 parts = []
1084 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1084 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1085 if typ == b'string':
1085 if typ == b'string':
1086 parts.append(stringutil.escapestr(tmpl[start:end]))
1086 parts.append(stringutil.escapestr(tmpl[start:end]))
1087 else:
1087 else:
1088 parts.append(tmpl[start:end])
1088 parts.append(tmpl[start:end])
1089 return b''.join(parts)
1089 return b''.join(parts)
1090
1090
1091
1091
1092 def rendercommandtemplate(ui, tmpl, props):
1092 def rendercommandtemplate(ui, tmpl, props):
1093 r"""Expand a literal template 'tmpl' in a way suitable for command line
1093 r"""Expand a literal template 'tmpl' in a way suitable for command line
1094
1094
1095 '\' in outermost string is not taken as an escape character because it
1095 '\' in outermost string is not taken as an escape character because it
1096 is a directory separator on Windows.
1096 is a directory separator on Windows.
1097
1097
1098 >>> from . import ui as uimod
1098 >>> from . import ui as uimod
1099 >>> ui = uimod.ui()
1099 >>> ui = uimod.ui()
1100 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1100 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1101 'c:\\foo'
1101 'c:\\foo'
1102 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1102 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1103 'c:{path}'
1103 'c:{path}'
1104 """
1104 """
1105 if not tmpl:
1105 if not tmpl:
1106 return tmpl
1106 return tmpl
1107 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1107 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1108 return t.renderdefault(props)
1108 return t.renderdefault(props)
1109
1109
1110
1110
1111 def rendertemplate(ctx, tmpl, props=None):
1111 def rendertemplate(ctx, tmpl, props=None):
1112 """Expand a literal template 'tmpl' byte-string against one changeset
1112 """Expand a literal template 'tmpl' byte-string against one changeset
1113
1113
1114 Each props item must be a stringify-able value or a callable returning
1114 Each props item must be a stringify-able value or a callable returning
1115 such value, i.e. no bare list nor dict should be passed.
1115 such value, i.e. no bare list nor dict should be passed.
1116 """
1116 """
1117 repo = ctx.repo()
1117 repo = ctx.repo()
1118 tres = formatter.templateresources(repo.ui, repo)
1118 tres = formatter.templateresources(repo.ui, repo)
1119 t = formatter.maketemplater(
1119 t = formatter.maketemplater(
1120 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1120 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1121 )
1121 )
1122 mapping = {b'ctx': ctx}
1122 mapping = {b'ctx': ctx}
1123 if props:
1123 if props:
1124 mapping.update(props)
1124 mapping.update(props)
1125 return t.renderdefault(mapping)
1125 return t.renderdefault(mapping)
1126
1126
1127
1127
1128 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1128 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1129 r"""Convert old-style filename format string to template string
1129 r"""Convert old-style filename format string to template string
1130
1130
1131 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1131 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1132 'foo-{reporoot|basename}-{seqno}.patch'
1132 'foo-{reporoot|basename}-{seqno}.patch'
1133 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1133 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1134 '{rev}{tags % "{tag}"}{node}'
1134 '{rev}{tags % "{tag}"}{node}'
1135
1135
1136 '\' in outermost strings has to be escaped because it is a directory
1136 '\' in outermost strings has to be escaped because it is a directory
1137 separator on Windows:
1137 separator on Windows:
1138
1138
1139 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1139 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1140 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1140 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1141 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1141 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1142 '\\\\\\\\foo\\\\bar.patch'
1142 '\\\\\\\\foo\\\\bar.patch'
1143 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1143 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1144 '\\\\{tags % "{tag}"}'
1144 '\\\\{tags % "{tag}"}'
1145
1145
1146 but inner strings follow the template rules (i.e. '\' is taken as an
1146 but inner strings follow the template rules (i.e. '\' is taken as an
1147 escape character):
1147 escape character):
1148
1148
1149 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1149 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1150 '{"c:\\tmp"}'
1150 '{"c:\\tmp"}'
1151 """
1151 """
1152 expander = {
1152 expander = {
1153 b'H': b'{node}',
1153 b'H': b'{node}',
1154 b'R': b'{rev}',
1154 b'R': b'{rev}',
1155 b'h': b'{node|short}',
1155 b'h': b'{node|short}',
1156 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1156 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1157 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1157 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1158 b'%': b'%',
1158 b'%': b'%',
1159 b'b': b'{reporoot|basename}',
1159 b'b': b'{reporoot|basename}',
1160 }
1160 }
1161 if total is not None:
1161 if total is not None:
1162 expander[b'N'] = b'{total}'
1162 expander[b'N'] = b'{total}'
1163 if seqno is not None:
1163 if seqno is not None:
1164 expander[b'n'] = b'{seqno}'
1164 expander[b'n'] = b'{seqno}'
1165 if total is not None and seqno is not None:
1165 if total is not None and seqno is not None:
1166 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1166 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1167 if pathname is not None:
1167 if pathname is not None:
1168 expander[b's'] = b'{pathname|basename}'
1168 expander[b's'] = b'{pathname|basename}'
1169 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1169 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1170 expander[b'p'] = b'{pathname}'
1170 expander[b'p'] = b'{pathname}'
1171
1171
1172 newname = []
1172 newname = []
1173 for typ, start, end in templater.scantemplate(pat, raw=True):
1173 for typ, start, end in templater.scantemplate(pat, raw=True):
1174 if typ != b'string':
1174 if typ != b'string':
1175 newname.append(pat[start:end])
1175 newname.append(pat[start:end])
1176 continue
1176 continue
1177 i = start
1177 i = start
1178 while i < end:
1178 while i < end:
1179 n = pat.find(b'%', i, end)
1179 n = pat.find(b'%', i, end)
1180 if n < 0:
1180 if n < 0:
1181 newname.append(stringutil.escapestr(pat[i:end]))
1181 newname.append(stringutil.escapestr(pat[i:end]))
1182 break
1182 break
1183 newname.append(stringutil.escapestr(pat[i:n]))
1183 newname.append(stringutil.escapestr(pat[i:n]))
1184 if n + 2 > end:
1184 if n + 2 > end:
1185 raise error.Abort(
1185 raise error.Abort(
1186 _(b"incomplete format spec in output filename")
1186 _(b"incomplete format spec in output filename")
1187 )
1187 )
1188 c = pat[n + 1 : n + 2]
1188 c = pat[n + 1 : n + 2]
1189 i = n + 2
1189 i = n + 2
1190 try:
1190 try:
1191 newname.append(expander[c])
1191 newname.append(expander[c])
1192 except KeyError:
1192 except KeyError:
1193 raise error.Abort(
1193 raise error.Abort(
1194 _(b"invalid format spec '%%%s' in output filename") % c
1194 _(b"invalid format spec '%%%s' in output filename") % c
1195 )
1195 )
1196 return b''.join(newname)
1196 return b''.join(newname)
1197
1197
1198
1198
1199 def makefilename(ctx, pat, **props):
1199 def makefilename(ctx, pat, **props):
1200 if not pat:
1200 if not pat:
1201 return pat
1201 return pat
1202 tmpl = _buildfntemplate(pat, **props)
1202 tmpl = _buildfntemplate(pat, **props)
1203 # BUG: alias expansion shouldn't be made against template fragments
1203 # BUG: alias expansion shouldn't be made against template fragments
1204 # rewritten from %-format strings, but we have no easy way to partially
1204 # rewritten from %-format strings, but we have no easy way to partially
1205 # disable the expansion.
1205 # disable the expansion.
1206 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1206 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1207
1207
1208
1208
1209 def isstdiofilename(pat):
1209 def isstdiofilename(pat):
1210 """True if the given pat looks like a filename denoting stdin/stdout"""
1210 """True if the given pat looks like a filename denoting stdin/stdout"""
1211 return not pat or pat == b'-'
1211 return not pat or pat == b'-'
1212
1212
1213
1213
1214 class _unclosablefile(object):
1214 class _unclosablefile(object):
1215 def __init__(self, fp):
1215 def __init__(self, fp):
1216 self._fp = fp
1216 self._fp = fp
1217
1217
1218 def close(self):
1218 def close(self):
1219 pass
1219 pass
1220
1220
1221 def __iter__(self):
1221 def __iter__(self):
1222 return iter(self._fp)
1222 return iter(self._fp)
1223
1223
1224 def __getattr__(self, attr):
1224 def __getattr__(self, attr):
1225 return getattr(self._fp, attr)
1225 return getattr(self._fp, attr)
1226
1226
1227 def __enter__(self):
1227 def __enter__(self):
1228 return self
1228 return self
1229
1229
1230 def __exit__(self, exc_type, exc_value, exc_tb):
1230 def __exit__(self, exc_type, exc_value, exc_tb):
1231 pass
1231 pass
1232
1232
1233
1233
1234 def makefileobj(ctx, pat, mode=b'wb', **props):
1234 def makefileobj(ctx, pat, mode=b'wb', **props):
1235 writable = mode not in (b'r', b'rb')
1235 writable = mode not in (b'r', b'rb')
1236
1236
1237 if isstdiofilename(pat):
1237 if isstdiofilename(pat):
1238 repo = ctx.repo()
1238 repo = ctx.repo()
1239 if writable:
1239 if writable:
1240 fp = repo.ui.fout
1240 fp = repo.ui.fout
1241 else:
1241 else:
1242 fp = repo.ui.fin
1242 fp = repo.ui.fin
1243 return _unclosablefile(fp)
1243 return _unclosablefile(fp)
1244 fn = makefilename(ctx, pat, **props)
1244 fn = makefilename(ctx, pat, **props)
1245 return open(fn, mode)
1245 return open(fn, mode)
1246
1246
1247
1247
1248 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1248 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1249 """opens the changelog, manifest, a filelog or a given revlog"""
1249 """opens the changelog, manifest, a filelog or a given revlog"""
1250 cl = opts[b'changelog']
1250 cl = opts[b'changelog']
1251 mf = opts[b'manifest']
1251 mf = opts[b'manifest']
1252 dir = opts[b'dir']
1252 dir = opts[b'dir']
1253 msg = None
1253 msg = None
1254 if cl and mf:
1254 if cl and mf:
1255 msg = _(b'cannot specify --changelog and --manifest at the same time')
1255 msg = _(b'cannot specify --changelog and --manifest at the same time')
1256 elif cl and dir:
1256 elif cl and dir:
1257 msg = _(b'cannot specify --changelog and --dir at the same time')
1257 msg = _(b'cannot specify --changelog and --dir at the same time')
1258 elif cl or mf or dir:
1258 elif cl or mf or dir:
1259 if file_:
1259 if file_:
1260 msg = _(b'cannot specify filename with --changelog or --manifest')
1260 msg = _(b'cannot specify filename with --changelog or --manifest')
1261 elif not repo:
1261 elif not repo:
1262 msg = _(
1262 msg = _(
1263 b'cannot specify --changelog or --manifest or --dir '
1263 b'cannot specify --changelog or --manifest or --dir '
1264 b'without a repository'
1264 b'without a repository'
1265 )
1265 )
1266 if msg:
1266 if msg:
1267 raise error.Abort(msg)
1267 raise error.Abort(msg)
1268
1268
1269 r = None
1269 r = None
1270 if repo:
1270 if repo:
1271 if cl:
1271 if cl:
1272 r = repo.unfiltered().changelog
1272 r = repo.unfiltered().changelog
1273 elif dir:
1273 elif dir:
1274 if b'treemanifest' not in repo.requirements:
1274 if b'treemanifest' not in repo.requirements:
1275 raise error.Abort(
1275 raise error.Abort(
1276 _(
1276 _(
1277 b"--dir can only be used on repos with "
1277 b"--dir can only be used on repos with "
1278 b"treemanifest enabled"
1278 b"treemanifest enabled"
1279 )
1279 )
1280 )
1280 )
1281 if not dir.endswith(b'/'):
1281 if not dir.endswith(b'/'):
1282 dir = dir + b'/'
1282 dir = dir + b'/'
1283 dirlog = repo.manifestlog.getstorage(dir)
1283 dirlog = repo.manifestlog.getstorage(dir)
1284 if len(dirlog):
1284 if len(dirlog):
1285 r = dirlog
1285 r = dirlog
1286 elif mf:
1286 elif mf:
1287 r = repo.manifestlog.getstorage(b'')
1287 r = repo.manifestlog.getstorage(b'')
1288 elif file_:
1288 elif file_:
1289 filelog = repo.file(file_)
1289 filelog = repo.file(file_)
1290 if len(filelog):
1290 if len(filelog):
1291 r = filelog
1291 r = filelog
1292
1292
1293 # Not all storage may be revlogs. If requested, try to return an actual
1293 # Not all storage may be revlogs. If requested, try to return an actual
1294 # revlog instance.
1294 # revlog instance.
1295 if returnrevlog:
1295 if returnrevlog:
1296 if isinstance(r, revlog.revlog):
1296 if isinstance(r, revlog.revlog):
1297 pass
1297 pass
1298 elif util.safehasattr(r, b'_revlog'):
1298 elif util.safehasattr(r, b'_revlog'):
1299 r = r._revlog # pytype: disable=attribute-error
1299 r = r._revlog # pytype: disable=attribute-error
1300 elif r is not None:
1300 elif r is not None:
1301 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1301 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1302
1302
1303 if not r:
1303 if not r:
1304 if not returnrevlog:
1304 if not returnrevlog:
1305 raise error.Abort(_(b'cannot give path to non-revlog'))
1305 raise error.Abort(_(b'cannot give path to non-revlog'))
1306
1306
1307 if not file_:
1307 if not file_:
1308 raise error.CommandError(cmd, _(b'invalid arguments'))
1308 raise error.CommandError(cmd, _(b'invalid arguments'))
1309 if not os.path.isfile(file_):
1309 if not os.path.isfile(file_):
1310 raise error.Abort(_(b"revlog '%s' not found") % file_)
1310 raise error.Abort(_(b"revlog '%s' not found") % file_)
1311 r = revlog.revlog(
1311 r = revlog.revlog(
1312 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1312 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1313 )
1313 )
1314 return r
1314 return r
1315
1315
1316
1316
1317 def openrevlog(repo, cmd, file_, opts):
1317 def openrevlog(repo, cmd, file_, opts):
1318 """Obtain a revlog backing storage of an item.
1318 """Obtain a revlog backing storage of an item.
1319
1319
1320 This is similar to ``openstorage()`` except it always returns a revlog.
1320 This is similar to ``openstorage()`` except it always returns a revlog.
1321
1321
1322 In most cases, a caller cares about the main storage object - not the
1322 In most cases, a caller cares about the main storage object - not the
1323 revlog backing it. Therefore, this function should only be used by code
1323 revlog backing it. Therefore, this function should only be used by code
1324 that needs to examine low-level revlog implementation details. e.g. debug
1324 that needs to examine low-level revlog implementation details. e.g. debug
1325 commands.
1325 commands.
1326 """
1326 """
1327 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1327 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1328
1328
1329
1329
1330 def copy(ui, repo, pats, opts, rename=False):
1330 def copy(ui, repo, pats, opts, rename=False):
1331 # called with the repo lock held
1331 # called with the repo lock held
1332 #
1332 #
1333 # hgsep => pathname that uses "/" to separate directories
1333 # hgsep => pathname that uses "/" to separate directories
1334 # ossep => pathname that uses os.sep to separate directories
1334 # ossep => pathname that uses os.sep to separate directories
1335 cwd = repo.getcwd()
1335 cwd = repo.getcwd()
1336 targets = {}
1336 targets = {}
1337 after = opts.get(b"after")
1337 after = opts.get(b"after")
1338 dryrun = opts.get(b"dry_run")
1338 dryrun = opts.get(b"dry_run")
1339 wctx = repo[None]
1339 wctx = repo[None]
1340
1340
1341 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1341 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1342
1342
1343 def walkpat(pat):
1343 def walkpat(pat):
1344 srcs = []
1344 srcs = []
1345 if after:
1345 if after:
1346 badstates = b'?'
1346 badstates = b'?'
1347 else:
1347 else:
1348 badstates = b'?r'
1348 badstates = b'?r'
1349 m = scmutil.match(wctx, [pat], opts, globbed=True)
1349 m = scmutil.match(wctx, [pat], opts, globbed=True)
1350 for abs in wctx.walk(m):
1350 for abs in wctx.walk(m):
1351 state = repo.dirstate[abs]
1351 state = repo.dirstate[abs]
1352 rel = uipathfn(abs)
1352 rel = uipathfn(abs)
1353 exact = m.exact(abs)
1353 exact = m.exact(abs)
1354 if state in badstates:
1354 if state in badstates:
1355 if exact and state == b'?':
1355 if exact and state == b'?':
1356 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1356 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1357 if exact and state == b'r':
1357 if exact and state == b'r':
1358 ui.warn(
1358 ui.warn(
1359 _(
1359 _(
1360 b'%s: not copying - file has been marked for'
1360 b'%s: not copying - file has been marked for'
1361 b' remove\n'
1361 b' remove\n'
1362 )
1362 )
1363 % rel
1363 % rel
1364 )
1364 )
1365 continue
1365 continue
1366 # abs: hgsep
1366 # abs: hgsep
1367 # rel: ossep
1367 # rel: ossep
1368 srcs.append((abs, rel, exact))
1368 srcs.append((abs, rel, exact))
1369 return srcs
1369 return srcs
1370
1370
1371 # abssrc: hgsep
1371 # abssrc: hgsep
1372 # relsrc: ossep
1372 # relsrc: ossep
1373 # otarget: ossep
1373 # otarget: ossep
1374 def copyfile(abssrc, relsrc, otarget, exact):
1374 def copyfile(abssrc, relsrc, otarget, exact):
1375 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1375 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1376 if b'/' in abstarget:
1376 if b'/' in abstarget:
1377 # We cannot normalize abstarget itself, this would prevent
1377 # We cannot normalize abstarget itself, this would prevent
1378 # case only renames, like a => A.
1378 # case only renames, like a => A.
1379 abspath, absname = abstarget.rsplit(b'/', 1)
1379 abspath, absname = abstarget.rsplit(b'/', 1)
1380 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1380 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1381 reltarget = repo.pathto(abstarget, cwd)
1381 reltarget = repo.pathto(abstarget, cwd)
1382 target = repo.wjoin(abstarget)
1382 target = repo.wjoin(abstarget)
1383 src = repo.wjoin(abssrc)
1383 src = repo.wjoin(abssrc)
1384 state = repo.dirstate[abstarget]
1384 state = repo.dirstate[abstarget]
1385
1385
1386 scmutil.checkportable(ui, abstarget)
1386 scmutil.checkportable(ui, abstarget)
1387
1387
1388 # check for collisions
1388 # check for collisions
1389 prevsrc = targets.get(abstarget)
1389 prevsrc = targets.get(abstarget)
1390 if prevsrc is not None:
1390 if prevsrc is not None:
1391 ui.warn(
1391 ui.warn(
1392 _(b'%s: not overwriting - %s collides with %s\n')
1392 _(b'%s: not overwriting - %s collides with %s\n')
1393 % (
1393 % (
1394 reltarget,
1394 reltarget,
1395 repo.pathto(abssrc, cwd),
1395 repo.pathto(abssrc, cwd),
1396 repo.pathto(prevsrc, cwd),
1396 repo.pathto(prevsrc, cwd),
1397 )
1397 )
1398 )
1398 )
1399 return True # report a failure
1399 return True # report a failure
1400
1400
1401 # check for overwrites
1401 # check for overwrites
1402 exists = os.path.lexists(target)
1402 exists = os.path.lexists(target)
1403 samefile = False
1403 samefile = False
1404 if exists and abssrc != abstarget:
1404 if exists and abssrc != abstarget:
1405 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1405 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1406 abstarget
1406 abstarget
1407 ):
1407 ):
1408 if not rename:
1408 if not rename:
1409 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1409 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1410 return True # report a failure
1410 return True # report a failure
1411 exists = False
1411 exists = False
1412 samefile = True
1412 samefile = True
1413
1413
1414 if not after and exists or after and state in b'mn':
1414 if not after and exists or after and state in b'mn':
1415 if not opts[b'force']:
1415 if not opts[b'force']:
1416 if state in b'mn':
1416 if state in b'mn':
1417 msg = _(b'%s: not overwriting - file already committed\n')
1417 msg = _(b'%s: not overwriting - file already committed\n')
1418 if after:
1418 if after:
1419 flags = b'--after --force'
1419 flags = b'--after --force'
1420 else:
1420 else:
1421 flags = b'--force'
1421 flags = b'--force'
1422 if rename:
1422 if rename:
1423 hint = (
1423 hint = (
1424 _(
1424 _(
1425 b"('hg rename %s' to replace the file by "
1425 b"('hg rename %s' to replace the file by "
1426 b'recording a rename)\n'
1426 b'recording a rename)\n'
1427 )
1427 )
1428 % flags
1428 % flags
1429 )
1429 )
1430 else:
1430 else:
1431 hint = (
1431 hint = (
1432 _(
1432 _(
1433 b"('hg copy %s' to replace the file by "
1433 b"('hg copy %s' to replace the file by "
1434 b'recording a copy)\n'
1434 b'recording a copy)\n'
1435 )
1435 )
1436 % flags
1436 % flags
1437 )
1437 )
1438 else:
1438 else:
1439 msg = _(b'%s: not overwriting - file exists\n')
1439 msg = _(b'%s: not overwriting - file exists\n')
1440 if rename:
1440 if rename:
1441 hint = _(
1441 hint = _(
1442 b"('hg rename --after' to record the rename)\n"
1442 b"('hg rename --after' to record the rename)\n"
1443 )
1443 )
1444 else:
1444 else:
1445 hint = _(b"('hg copy --after' to record the copy)\n")
1445 hint = _(b"('hg copy --after' to record the copy)\n")
1446 ui.warn(msg % reltarget)
1446 ui.warn(msg % reltarget)
1447 ui.warn(hint)
1447 ui.warn(hint)
1448 return True # report a failure
1448 return True # report a failure
1449
1449
1450 if after:
1450 if after:
1451 if not exists:
1451 if not exists:
1452 if rename:
1452 if rename:
1453 ui.warn(
1453 ui.warn(
1454 _(b'%s: not recording move - %s does not exist\n')
1454 _(b'%s: not recording move - %s does not exist\n')
1455 % (relsrc, reltarget)
1455 % (relsrc, reltarget)
1456 )
1456 )
1457 else:
1457 else:
1458 ui.warn(
1458 ui.warn(
1459 _(b'%s: not recording copy - %s does not exist\n')
1459 _(b'%s: not recording copy - %s does not exist\n')
1460 % (relsrc, reltarget)
1460 % (relsrc, reltarget)
1461 )
1461 )
1462 return True # report a failure
1462 return True # report a failure
1463 elif not dryrun:
1463 elif not dryrun:
1464 try:
1464 try:
1465 if exists:
1465 if exists:
1466 os.unlink(target)
1466 os.unlink(target)
1467 targetdir = os.path.dirname(target) or b'.'
1467 targetdir = os.path.dirname(target) or b'.'
1468 if not os.path.isdir(targetdir):
1468 if not os.path.isdir(targetdir):
1469 os.makedirs(targetdir)
1469 os.makedirs(targetdir)
1470 if samefile:
1470 if samefile:
1471 tmp = target + b"~hgrename"
1471 tmp = target + b"~hgrename"
1472 os.rename(src, tmp)
1472 os.rename(src, tmp)
1473 os.rename(tmp, target)
1473 os.rename(tmp, target)
1474 else:
1474 else:
1475 # Preserve stat info on renames, not on copies; this matches
1475 # Preserve stat info on renames, not on copies; this matches
1476 # Linux CLI behavior.
1476 # Linux CLI behavior.
1477 util.copyfile(src, target, copystat=rename)
1477 util.copyfile(src, target, copystat=rename)
1478 srcexists = True
1478 srcexists = True
1479 except IOError as inst:
1479 except IOError as inst:
1480 if inst.errno == errno.ENOENT:
1480 if inst.errno == errno.ENOENT:
1481 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1481 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1482 srcexists = False
1482 srcexists = False
1483 else:
1483 else:
1484 ui.warn(
1484 ui.warn(
1485 _(b'%s: cannot copy - %s\n')
1485 _(b'%s: cannot copy - %s\n')
1486 % (relsrc, encoding.strtolocal(inst.strerror))
1486 % (relsrc, encoding.strtolocal(inst.strerror))
1487 )
1487 )
1488 return True # report a failure
1488 return True # report a failure
1489
1489
1490 if ui.verbose or not exact:
1490 if ui.verbose or not exact:
1491 if rename:
1491 if rename:
1492 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1492 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1493 else:
1493 else:
1494 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1494 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1495
1495
1496 targets[abstarget] = abssrc
1496 targets[abstarget] = abssrc
1497
1497
1498 # fix up dirstate
1498 # fix up dirstate
1499 scmutil.dirstatecopy(
1499 scmutil.dirstatecopy(
1500 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1500 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1501 )
1501 )
1502 if rename and not dryrun:
1502 if rename and not dryrun:
1503 if not after and srcexists and not samefile:
1503 if not after and srcexists and not samefile:
1504 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1504 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1505 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1505 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1506 wctx.forget([abssrc])
1506 wctx.forget([abssrc])
1507
1507
1508 # pat: ossep
1508 # pat: ossep
1509 # dest ossep
1509 # dest ossep
1510 # srcs: list of (hgsep, hgsep, ossep, bool)
1510 # srcs: list of (hgsep, hgsep, ossep, bool)
1511 # return: function that takes hgsep and returns ossep
1511 # return: function that takes hgsep and returns ossep
1512 def targetpathfn(pat, dest, srcs):
1512 def targetpathfn(pat, dest, srcs):
1513 if os.path.isdir(pat):
1513 if os.path.isdir(pat):
1514 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1514 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1515 abspfx = util.localpath(abspfx)
1515 abspfx = util.localpath(abspfx)
1516 if destdirexists:
1516 if destdirexists:
1517 striplen = len(os.path.split(abspfx)[0])
1517 striplen = len(os.path.split(abspfx)[0])
1518 else:
1518 else:
1519 striplen = len(abspfx)
1519 striplen = len(abspfx)
1520 if striplen:
1520 if striplen:
1521 striplen += len(pycompat.ossep)
1521 striplen += len(pycompat.ossep)
1522 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1522 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1523 elif destdirexists:
1523 elif destdirexists:
1524 res = lambda p: os.path.join(
1524 res = lambda p: os.path.join(
1525 dest, os.path.basename(util.localpath(p))
1525 dest, os.path.basename(util.localpath(p))
1526 )
1526 )
1527 else:
1527 else:
1528 res = lambda p: dest
1528 res = lambda p: dest
1529 return res
1529 return res
1530
1530
1531 # pat: ossep
1531 # pat: ossep
1532 # dest ossep
1532 # dest ossep
1533 # srcs: list of (hgsep, hgsep, ossep, bool)
1533 # srcs: list of (hgsep, hgsep, ossep, bool)
1534 # return: function that takes hgsep and returns ossep
1534 # return: function that takes hgsep and returns ossep
1535 def targetpathafterfn(pat, dest, srcs):
1535 def targetpathafterfn(pat, dest, srcs):
1536 if matchmod.patkind(pat):
1536 if matchmod.patkind(pat):
1537 # a mercurial pattern
1537 # a mercurial pattern
1538 res = lambda p: os.path.join(
1538 res = lambda p: os.path.join(
1539 dest, os.path.basename(util.localpath(p))
1539 dest, os.path.basename(util.localpath(p))
1540 )
1540 )
1541 else:
1541 else:
1542 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1542 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1543 if len(abspfx) < len(srcs[0][0]):
1543 if len(abspfx) < len(srcs[0][0]):
1544 # A directory. Either the target path contains the last
1544 # A directory. Either the target path contains the last
1545 # component of the source path or it does not.
1545 # component of the source path or it does not.
1546 def evalpath(striplen):
1546 def evalpath(striplen):
1547 score = 0
1547 score = 0
1548 for s in srcs:
1548 for s in srcs:
1549 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1549 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1550 if os.path.lexists(t):
1550 if os.path.lexists(t):
1551 score += 1
1551 score += 1
1552 return score
1552 return score
1553
1553
1554 abspfx = util.localpath(abspfx)
1554 abspfx = util.localpath(abspfx)
1555 striplen = len(abspfx)
1555 striplen = len(abspfx)
1556 if striplen:
1556 if striplen:
1557 striplen += len(pycompat.ossep)
1557 striplen += len(pycompat.ossep)
1558 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1558 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1559 score = evalpath(striplen)
1559 score = evalpath(striplen)
1560 striplen1 = len(os.path.split(abspfx)[0])
1560 striplen1 = len(os.path.split(abspfx)[0])
1561 if striplen1:
1561 if striplen1:
1562 striplen1 += len(pycompat.ossep)
1562 striplen1 += len(pycompat.ossep)
1563 if evalpath(striplen1) > score:
1563 if evalpath(striplen1) > score:
1564 striplen = striplen1
1564 striplen = striplen1
1565 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1565 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1566 else:
1566 else:
1567 # a file
1567 # a file
1568 if destdirexists:
1568 if destdirexists:
1569 res = lambda p: os.path.join(
1569 res = lambda p: os.path.join(
1570 dest, os.path.basename(util.localpath(p))
1570 dest, os.path.basename(util.localpath(p))
1571 )
1571 )
1572 else:
1572 else:
1573 res = lambda p: dest
1573 res = lambda p: dest
1574 return res
1574 return res
1575
1575
1576 pats = scmutil.expandpats(pats)
1576 pats = scmutil.expandpats(pats)
1577 if not pats:
1577 if not pats:
1578 raise error.Abort(_(b'no source or destination specified'))
1578 raise error.Abort(_(b'no source or destination specified'))
1579 if len(pats) == 1:
1579 if len(pats) == 1:
1580 raise error.Abort(_(b'no destination specified'))
1580 raise error.Abort(_(b'no destination specified'))
1581 dest = pats.pop()
1581 dest = pats.pop()
1582 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1582 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1583 if not destdirexists:
1583 if not destdirexists:
1584 if len(pats) > 1 or matchmod.patkind(pats[0]):
1584 if len(pats) > 1 or matchmod.patkind(pats[0]):
1585 raise error.Abort(
1585 raise error.Abort(
1586 _(
1586 _(
1587 b'with multiple sources, destination must be an '
1587 b'with multiple sources, destination must be an '
1588 b'existing directory'
1588 b'existing directory'
1589 )
1589 )
1590 )
1590 )
1591 if util.endswithsep(dest):
1591 if util.endswithsep(dest):
1592 raise error.Abort(_(b'destination %s is not a directory') % dest)
1592 raise error.Abort(_(b'destination %s is not a directory') % dest)
1593
1593
1594 tfn = targetpathfn
1594 tfn = targetpathfn
1595 if after:
1595 if after:
1596 tfn = targetpathafterfn
1596 tfn = targetpathafterfn
1597 copylist = []
1597 copylist = []
1598 for pat in pats:
1598 for pat in pats:
1599 srcs = walkpat(pat)
1599 srcs = walkpat(pat)
1600 if not srcs:
1600 if not srcs:
1601 continue
1601 continue
1602 copylist.append((tfn(pat, dest, srcs), srcs))
1602 copylist.append((tfn(pat, dest, srcs), srcs))
1603 if not copylist:
1603 if not copylist:
1604 raise error.Abort(_(b'no files to copy'))
1604 raise error.Abort(_(b'no files to copy'))
1605
1605
1606 errors = 0
1606 errors = 0
1607 for targetpath, srcs in copylist:
1607 for targetpath, srcs in copylist:
1608 for abssrc, relsrc, exact in srcs:
1608 for abssrc, relsrc, exact in srcs:
1609 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1609 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1610 errors += 1
1610 errors += 1
1611
1611
1612 return errors != 0
1612 return errors != 0
1613
1613
1614
1614
1615 ## facility to let extension process additional data into an import patch
1615 ## facility to let extension process additional data into an import patch
1616 # list of identifier to be executed in order
1616 # list of identifier to be executed in order
1617 extrapreimport = [] # run before commit
1617 extrapreimport = [] # run before commit
1618 extrapostimport = [] # run after commit
1618 extrapostimport = [] # run after commit
1619 # mapping from identifier to actual import function
1619 # mapping from identifier to actual import function
1620 #
1620 #
1621 # 'preimport' are run before the commit is made and are provided the following
1621 # 'preimport' are run before the commit is made and are provided the following
1622 # arguments:
1622 # arguments:
1623 # - repo: the localrepository instance,
1623 # - repo: the localrepository instance,
1624 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1624 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1625 # - extra: the future extra dictionary of the changeset, please mutate it,
1625 # - extra: the future extra dictionary of the changeset, please mutate it,
1626 # - opts: the import options.
1626 # - opts: the import options.
1627 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1627 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1628 # mutation of in memory commit and more. Feel free to rework the code to get
1628 # mutation of in memory commit and more. Feel free to rework the code to get
1629 # there.
1629 # there.
1630 extrapreimportmap = {}
1630 extrapreimportmap = {}
1631 # 'postimport' are run after the commit is made and are provided the following
1631 # 'postimport' are run after the commit is made and are provided the following
1632 # argument:
1632 # argument:
1633 # - ctx: the changectx created by import.
1633 # - ctx: the changectx created by import.
1634 extrapostimportmap = {}
1634 extrapostimportmap = {}
1635
1635
1636
1636
1637 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1637 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1638 """Utility function used by commands.import to import a single patch
1638 """Utility function used by commands.import to import a single patch
1639
1639
1640 This function is explicitly defined here to help the evolve extension to
1640 This function is explicitly defined here to help the evolve extension to
1641 wrap this part of the import logic.
1641 wrap this part of the import logic.
1642
1642
1643 The API is currently a bit ugly because it a simple code translation from
1643 The API is currently a bit ugly because it a simple code translation from
1644 the import command. Feel free to make it better.
1644 the import command. Feel free to make it better.
1645
1645
1646 :patchdata: a dictionary containing parsed patch data (such as from
1646 :patchdata: a dictionary containing parsed patch data (such as from
1647 ``patch.extract()``)
1647 ``patch.extract()``)
1648 :parents: nodes that will be parent of the created commit
1648 :parents: nodes that will be parent of the created commit
1649 :opts: the full dict of option passed to the import command
1649 :opts: the full dict of option passed to the import command
1650 :msgs: list to save commit message to.
1650 :msgs: list to save commit message to.
1651 (used in case we need to save it when failing)
1651 (used in case we need to save it when failing)
1652 :updatefunc: a function that update a repo to a given node
1652 :updatefunc: a function that update a repo to a given node
1653 updatefunc(<repo>, <node>)
1653 updatefunc(<repo>, <node>)
1654 """
1654 """
1655 # avoid cycle context -> subrepo -> cmdutil
1655 # avoid cycle context -> subrepo -> cmdutil
1656 from . import context
1656 from . import context
1657
1657
1658 tmpname = patchdata.get(b'filename')
1658 tmpname = patchdata.get(b'filename')
1659 message = patchdata.get(b'message')
1659 message = patchdata.get(b'message')
1660 user = opts.get(b'user') or patchdata.get(b'user')
1660 user = opts.get(b'user') or patchdata.get(b'user')
1661 date = opts.get(b'date') or patchdata.get(b'date')
1661 date = opts.get(b'date') or patchdata.get(b'date')
1662 branch = patchdata.get(b'branch')
1662 branch = patchdata.get(b'branch')
1663 nodeid = patchdata.get(b'nodeid')
1663 nodeid = patchdata.get(b'nodeid')
1664 p1 = patchdata.get(b'p1')
1664 p1 = patchdata.get(b'p1')
1665 p2 = patchdata.get(b'p2')
1665 p2 = patchdata.get(b'p2')
1666
1666
1667 nocommit = opts.get(b'no_commit')
1667 nocommit = opts.get(b'no_commit')
1668 importbranch = opts.get(b'import_branch')
1668 importbranch = opts.get(b'import_branch')
1669 update = not opts.get(b'bypass')
1669 update = not opts.get(b'bypass')
1670 strip = opts[b"strip"]
1670 strip = opts[b"strip"]
1671 prefix = opts[b"prefix"]
1671 prefix = opts[b"prefix"]
1672 sim = float(opts.get(b'similarity') or 0)
1672 sim = float(opts.get(b'similarity') or 0)
1673
1673
1674 if not tmpname:
1674 if not tmpname:
1675 return None, None, False
1675 return None, None, False
1676
1676
1677 rejects = False
1677 rejects = False
1678
1678
1679 cmdline_message = logmessage(ui, opts)
1679 cmdline_message = logmessage(ui, opts)
1680 if cmdline_message:
1680 if cmdline_message:
1681 # pickup the cmdline msg
1681 # pickup the cmdline msg
1682 message = cmdline_message
1682 message = cmdline_message
1683 elif message:
1683 elif message:
1684 # pickup the patch msg
1684 # pickup the patch msg
1685 message = message.strip()
1685 message = message.strip()
1686 else:
1686 else:
1687 # launch the editor
1687 # launch the editor
1688 message = None
1688 message = None
1689 ui.debug(b'message:\n%s\n' % (message or b''))
1689 ui.debug(b'message:\n%s\n' % (message or b''))
1690
1690
1691 if len(parents) == 1:
1691 if len(parents) == 1:
1692 parents.append(repo[nullid])
1692 parents.append(repo[nullid])
1693 if opts.get(b'exact'):
1693 if opts.get(b'exact'):
1694 if not nodeid or not p1:
1694 if not nodeid or not p1:
1695 raise error.Abort(_(b'not a Mercurial patch'))
1695 raise error.Abort(_(b'not a Mercurial patch'))
1696 p1 = repo[p1]
1696 p1 = repo[p1]
1697 p2 = repo[p2 or nullid]
1697 p2 = repo[p2 or nullid]
1698 elif p2:
1698 elif p2:
1699 try:
1699 try:
1700 p1 = repo[p1]
1700 p1 = repo[p1]
1701 p2 = repo[p2]
1701 p2 = repo[p2]
1702 # Without any options, consider p2 only if the
1702 # Without any options, consider p2 only if the
1703 # patch is being applied on top of the recorded
1703 # patch is being applied on top of the recorded
1704 # first parent.
1704 # first parent.
1705 if p1 != parents[0]:
1705 if p1 != parents[0]:
1706 p1 = parents[0]
1706 p1 = parents[0]
1707 p2 = repo[nullid]
1707 p2 = repo[nullid]
1708 except error.RepoError:
1708 except error.RepoError:
1709 p1, p2 = parents
1709 p1, p2 = parents
1710 if p2.node() == nullid:
1710 if p2.node() == nullid:
1711 ui.warn(
1711 ui.warn(
1712 _(
1712 _(
1713 b"warning: import the patch as a normal revision\n"
1713 b"warning: import the patch as a normal revision\n"
1714 b"(use --exact to import the patch as a merge)\n"
1714 b"(use --exact to import the patch as a merge)\n"
1715 )
1715 )
1716 )
1716 )
1717 else:
1717 else:
1718 p1, p2 = parents
1718 p1, p2 = parents
1719
1719
1720 n = None
1720 n = None
1721 if update:
1721 if update:
1722 if p1 != parents[0]:
1722 if p1 != parents[0]:
1723 updatefunc(repo, p1.node())
1723 updatefunc(repo, p1.node())
1724 if p2 != parents[1]:
1724 if p2 != parents[1]:
1725 repo.setparents(p1.node(), p2.node())
1725 repo.setparents(p1.node(), p2.node())
1726
1726
1727 if opts.get(b'exact') or importbranch:
1727 if opts.get(b'exact') or importbranch:
1728 repo.dirstate.setbranch(branch or b'default')
1728 repo.dirstate.setbranch(branch or b'default')
1729
1729
1730 partial = opts.get(b'partial', False)
1730 partial = opts.get(b'partial', False)
1731 files = set()
1731 files = set()
1732 try:
1732 try:
1733 patch.patch(
1733 patch.patch(
1734 ui,
1734 ui,
1735 repo,
1735 repo,
1736 tmpname,
1736 tmpname,
1737 strip=strip,
1737 strip=strip,
1738 prefix=prefix,
1738 prefix=prefix,
1739 files=files,
1739 files=files,
1740 eolmode=None,
1740 eolmode=None,
1741 similarity=sim / 100.0,
1741 similarity=sim / 100.0,
1742 )
1742 )
1743 except error.PatchError as e:
1743 except error.PatchError as e:
1744 if not partial:
1744 if not partial:
1745 raise error.Abort(pycompat.bytestr(e))
1745 raise error.Abort(pycompat.bytestr(e))
1746 if partial:
1746 if partial:
1747 rejects = True
1747 rejects = True
1748
1748
1749 files = list(files)
1749 files = list(files)
1750 if nocommit:
1750 if nocommit:
1751 if message:
1751 if message:
1752 msgs.append(message)
1752 msgs.append(message)
1753 else:
1753 else:
1754 if opts.get(b'exact') or p2:
1754 if opts.get(b'exact') or p2:
1755 # If you got here, you either use --force and know what
1755 # If you got here, you either use --force and know what
1756 # you are doing or used --exact or a merge patch while
1756 # you are doing or used --exact or a merge patch while
1757 # being updated to its first parent.
1757 # being updated to its first parent.
1758 m = None
1758 m = None
1759 else:
1759 else:
1760 m = scmutil.matchfiles(repo, files or [])
1760 m = scmutil.matchfiles(repo, files or [])
1761 editform = mergeeditform(repo[None], b'import.normal')
1761 editform = mergeeditform(repo[None], b'import.normal')
1762 if opts.get(b'exact'):
1762 if opts.get(b'exact'):
1763 editor = None
1763 editor = None
1764 else:
1764 else:
1765 editor = getcommiteditor(
1765 editor = getcommiteditor(
1766 editform=editform, **pycompat.strkwargs(opts)
1766 editform=editform, **pycompat.strkwargs(opts)
1767 )
1767 )
1768 extra = {}
1768 extra = {}
1769 for idfunc in extrapreimport:
1769 for idfunc in extrapreimport:
1770 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1770 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1771 overrides = {}
1771 overrides = {}
1772 if partial:
1772 if partial:
1773 overrides[(b'ui', b'allowemptycommit')] = True
1773 overrides[(b'ui', b'allowemptycommit')] = True
1774 if opts.get(b'secret'):
1775 overrides[(b'phases', b'new-commit')] = b'secret'
1774 with repo.ui.configoverride(overrides, b'import'):
1776 with repo.ui.configoverride(overrides, b'import'):
1775 n = repo.commit(
1777 n = repo.commit(
1776 message, user, date, match=m, editor=editor, extra=extra
1778 message, user, date, match=m, editor=editor, extra=extra
1777 )
1779 )
1778 for idfunc in extrapostimport:
1780 for idfunc in extrapostimport:
1779 extrapostimportmap[idfunc](repo[n])
1781 extrapostimportmap[idfunc](repo[n])
1780 else:
1782 else:
1781 if opts.get(b'exact') or importbranch:
1783 if opts.get(b'exact') or importbranch:
1782 branch = branch or b'default'
1784 branch = branch or b'default'
1783 else:
1785 else:
1784 branch = p1.branch()
1786 branch = p1.branch()
1785 store = patch.filestore()
1787 store = patch.filestore()
1786 try:
1788 try:
1787 files = set()
1789 files = set()
1788 try:
1790 try:
1789 patch.patchrepo(
1791 patch.patchrepo(
1790 ui,
1792 ui,
1791 repo,
1793 repo,
1792 p1,
1794 p1,
1793 store,
1795 store,
1794 tmpname,
1796 tmpname,
1795 strip,
1797 strip,
1796 prefix,
1798 prefix,
1797 files,
1799 files,
1798 eolmode=None,
1800 eolmode=None,
1799 )
1801 )
1800 except error.PatchError as e:
1802 except error.PatchError as e:
1801 raise error.Abort(stringutil.forcebytestr(e))
1803 raise error.Abort(stringutil.forcebytestr(e))
1802 if opts.get(b'exact'):
1804 if opts.get(b'exact'):
1803 editor = None
1805 editor = None
1804 else:
1806 else:
1805 editor = getcommiteditor(editform=b'import.bypass')
1807 editor = getcommiteditor(editform=b'import.bypass')
1806 memctx = context.memctx(
1808 memctx = context.memctx(
1807 repo,
1809 repo,
1808 (p1.node(), p2.node()),
1810 (p1.node(), p2.node()),
1809 message,
1811 message,
1810 files=files,
1812 files=files,
1811 filectxfn=store,
1813 filectxfn=store,
1812 user=user,
1814 user=user,
1813 date=date,
1815 date=date,
1814 branch=branch,
1816 branch=branch,
1815 editor=editor,
1817 editor=editor,
1816 )
1818 )
1817 n = memctx.commit()
1819 n = memctx.commit()
1818 finally:
1820 finally:
1819 store.close()
1821 store.close()
1820 if opts.get(b'exact') and nocommit:
1822 if opts.get(b'exact') and nocommit:
1821 # --exact with --no-commit is still useful in that it does merge
1823 # --exact with --no-commit is still useful in that it does merge
1822 # and branch bits
1824 # and branch bits
1823 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1825 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1824 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1826 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1825 raise error.Abort(_(b'patch is damaged or loses information'))
1827 raise error.Abort(_(b'patch is damaged or loses information'))
1826 msg = _(b'applied to working directory')
1828 msg = _(b'applied to working directory')
1827 if n:
1829 if n:
1828 # i18n: refers to a short changeset id
1830 # i18n: refers to a short changeset id
1829 msg = _(b'created %s') % short(n)
1831 msg = _(b'created %s') % short(n)
1830 return msg, n, rejects
1832 return msg, n, rejects
1831
1833
1832
1834
1833 # facility to let extensions include additional data in an exported patch
1835 # facility to let extensions include additional data in an exported patch
1834 # list of identifiers to be executed in order
1836 # list of identifiers to be executed in order
1835 extraexport = []
1837 extraexport = []
1836 # mapping from identifier to actual export function
1838 # mapping from identifier to actual export function
1837 # function as to return a string to be added to the header or None
1839 # function as to return a string to be added to the header or None
1838 # it is given two arguments (sequencenumber, changectx)
1840 # it is given two arguments (sequencenumber, changectx)
1839 extraexportmap = {}
1841 extraexportmap = {}
1840
1842
1841
1843
1842 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1844 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1843 node = scmutil.binnode(ctx)
1845 node = scmutil.binnode(ctx)
1844 parents = [p.node() for p in ctx.parents() if p]
1846 parents = [p.node() for p in ctx.parents() if p]
1845 branch = ctx.branch()
1847 branch = ctx.branch()
1846 if switch_parent:
1848 if switch_parent:
1847 parents.reverse()
1849 parents.reverse()
1848
1850
1849 if parents:
1851 if parents:
1850 prev = parents[0]
1852 prev = parents[0]
1851 else:
1853 else:
1852 prev = nullid
1854 prev = nullid
1853
1855
1854 fm.context(ctx=ctx)
1856 fm.context(ctx=ctx)
1855 fm.plain(b'# HG changeset patch\n')
1857 fm.plain(b'# HG changeset patch\n')
1856 fm.write(b'user', b'# User %s\n', ctx.user())
1858 fm.write(b'user', b'# User %s\n', ctx.user())
1857 fm.plain(b'# Date %d %d\n' % ctx.date())
1859 fm.plain(b'# Date %d %d\n' % ctx.date())
1858 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1860 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1859 fm.condwrite(
1861 fm.condwrite(
1860 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1862 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1861 )
1863 )
1862 fm.write(b'node', b'# Node ID %s\n', hex(node))
1864 fm.write(b'node', b'# Node ID %s\n', hex(node))
1863 fm.plain(b'# Parent %s\n' % hex(prev))
1865 fm.plain(b'# Parent %s\n' % hex(prev))
1864 if len(parents) > 1:
1866 if len(parents) > 1:
1865 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1867 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1866 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1868 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1867
1869
1868 # TODO: redesign extraexportmap function to support formatter
1870 # TODO: redesign extraexportmap function to support formatter
1869 for headerid in extraexport:
1871 for headerid in extraexport:
1870 header = extraexportmap[headerid](seqno, ctx)
1872 header = extraexportmap[headerid](seqno, ctx)
1871 if header is not None:
1873 if header is not None:
1872 fm.plain(b'# %s\n' % header)
1874 fm.plain(b'# %s\n' % header)
1873
1875
1874 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1876 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1875 fm.plain(b'\n')
1877 fm.plain(b'\n')
1876
1878
1877 if fm.isplain():
1879 if fm.isplain():
1878 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1880 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1879 for chunk, label in chunkiter:
1881 for chunk, label in chunkiter:
1880 fm.plain(chunk, label=label)
1882 fm.plain(chunk, label=label)
1881 else:
1883 else:
1882 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1884 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1883 # TODO: make it structured?
1885 # TODO: make it structured?
1884 fm.data(diff=b''.join(chunkiter))
1886 fm.data(diff=b''.join(chunkiter))
1885
1887
1886
1888
1887 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1889 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1888 """Export changesets to stdout or a single file"""
1890 """Export changesets to stdout or a single file"""
1889 for seqno, rev in enumerate(revs, 1):
1891 for seqno, rev in enumerate(revs, 1):
1890 ctx = repo[rev]
1892 ctx = repo[rev]
1891 if not dest.startswith(b'<'):
1893 if not dest.startswith(b'<'):
1892 repo.ui.note(b"%s\n" % dest)
1894 repo.ui.note(b"%s\n" % dest)
1893 fm.startitem()
1895 fm.startitem()
1894 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1896 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1895
1897
1896
1898
1897 def _exportfntemplate(
1899 def _exportfntemplate(
1898 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1900 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1899 ):
1901 ):
1900 """Export changesets to possibly multiple files"""
1902 """Export changesets to possibly multiple files"""
1901 total = len(revs)
1903 total = len(revs)
1902 revwidth = max(len(str(rev)) for rev in revs)
1904 revwidth = max(len(str(rev)) for rev in revs)
1903 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1905 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1904
1906
1905 for seqno, rev in enumerate(revs, 1):
1907 for seqno, rev in enumerate(revs, 1):
1906 ctx = repo[rev]
1908 ctx = repo[rev]
1907 dest = makefilename(
1909 dest = makefilename(
1908 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1910 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1909 )
1911 )
1910 filemap.setdefault(dest, []).append((seqno, rev))
1912 filemap.setdefault(dest, []).append((seqno, rev))
1911
1913
1912 for dest in filemap:
1914 for dest in filemap:
1913 with formatter.maybereopen(basefm, dest) as fm:
1915 with formatter.maybereopen(basefm, dest) as fm:
1914 repo.ui.note(b"%s\n" % dest)
1916 repo.ui.note(b"%s\n" % dest)
1915 for seqno, rev in filemap[dest]:
1917 for seqno, rev in filemap[dest]:
1916 fm.startitem()
1918 fm.startitem()
1917 ctx = repo[rev]
1919 ctx = repo[rev]
1918 _exportsingle(
1920 _exportsingle(
1919 repo, ctx, fm, match, switch_parent, seqno, diffopts
1921 repo, ctx, fm, match, switch_parent, seqno, diffopts
1920 )
1922 )
1921
1923
1922
1924
1923 def _prefetchchangedfiles(repo, revs, match):
1925 def _prefetchchangedfiles(repo, revs, match):
1924 allfiles = set()
1926 allfiles = set()
1925 for rev in revs:
1927 for rev in revs:
1926 for file in repo[rev].files():
1928 for file in repo[rev].files():
1927 if not match or match(file):
1929 if not match or match(file):
1928 allfiles.add(file)
1930 allfiles.add(file)
1929 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
1931 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
1930
1932
1931
1933
1932 def export(
1934 def export(
1933 repo,
1935 repo,
1934 revs,
1936 revs,
1935 basefm,
1937 basefm,
1936 fntemplate=b'hg-%h.patch',
1938 fntemplate=b'hg-%h.patch',
1937 switch_parent=False,
1939 switch_parent=False,
1938 opts=None,
1940 opts=None,
1939 match=None,
1941 match=None,
1940 ):
1942 ):
1941 '''export changesets as hg patches
1943 '''export changesets as hg patches
1942
1944
1943 Args:
1945 Args:
1944 repo: The repository from which we're exporting revisions.
1946 repo: The repository from which we're exporting revisions.
1945 revs: A list of revisions to export as revision numbers.
1947 revs: A list of revisions to export as revision numbers.
1946 basefm: A formatter to which patches should be written.
1948 basefm: A formatter to which patches should be written.
1947 fntemplate: An optional string to use for generating patch file names.
1949 fntemplate: An optional string to use for generating patch file names.
1948 switch_parent: If True, show diffs against second parent when not nullid.
1950 switch_parent: If True, show diffs against second parent when not nullid.
1949 Default is false, which always shows diff against p1.
1951 Default is false, which always shows diff against p1.
1950 opts: diff options to use for generating the patch.
1952 opts: diff options to use for generating the patch.
1951 match: If specified, only export changes to files matching this matcher.
1953 match: If specified, only export changes to files matching this matcher.
1952
1954
1953 Returns:
1955 Returns:
1954 Nothing.
1956 Nothing.
1955
1957
1956 Side Effect:
1958 Side Effect:
1957 "HG Changeset Patch" data is emitted to one of the following
1959 "HG Changeset Patch" data is emitted to one of the following
1958 destinations:
1960 destinations:
1959 fntemplate specified: Each rev is written to a unique file named using
1961 fntemplate specified: Each rev is written to a unique file named using
1960 the given template.
1962 the given template.
1961 Otherwise: All revs will be written to basefm.
1963 Otherwise: All revs will be written to basefm.
1962 '''
1964 '''
1963 _prefetchchangedfiles(repo, revs, match)
1965 _prefetchchangedfiles(repo, revs, match)
1964
1966
1965 if not fntemplate:
1967 if not fntemplate:
1966 _exportfile(
1968 _exportfile(
1967 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
1969 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
1968 )
1970 )
1969 else:
1971 else:
1970 _exportfntemplate(
1972 _exportfntemplate(
1971 repo, revs, basefm, fntemplate, switch_parent, opts, match
1973 repo, revs, basefm, fntemplate, switch_parent, opts, match
1972 )
1974 )
1973
1975
1974
1976
1975 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1977 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1976 """Export changesets to the given file stream"""
1978 """Export changesets to the given file stream"""
1977 _prefetchchangedfiles(repo, revs, match)
1979 _prefetchchangedfiles(repo, revs, match)
1978
1980
1979 dest = getattr(fp, 'name', b'<unnamed>')
1981 dest = getattr(fp, 'name', b'<unnamed>')
1980 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
1982 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
1981 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1983 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1982
1984
1983
1985
1984 def showmarker(fm, marker, index=None):
1986 def showmarker(fm, marker, index=None):
1985 """utility function to display obsolescence marker in a readable way
1987 """utility function to display obsolescence marker in a readable way
1986
1988
1987 To be used by debug function."""
1989 To be used by debug function."""
1988 if index is not None:
1990 if index is not None:
1989 fm.write(b'index', b'%i ', index)
1991 fm.write(b'index', b'%i ', index)
1990 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
1992 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
1991 succs = marker.succnodes()
1993 succs = marker.succnodes()
1992 fm.condwrite(
1994 fm.condwrite(
1993 succs,
1995 succs,
1994 b'succnodes',
1996 b'succnodes',
1995 b'%s ',
1997 b'%s ',
1996 fm.formatlist(map(hex, succs), name=b'node'),
1998 fm.formatlist(map(hex, succs), name=b'node'),
1997 )
1999 )
1998 fm.write(b'flag', b'%X ', marker.flags())
2000 fm.write(b'flag', b'%X ', marker.flags())
1999 parents = marker.parentnodes()
2001 parents = marker.parentnodes()
2000 if parents is not None:
2002 if parents is not None:
2001 fm.write(
2003 fm.write(
2002 b'parentnodes',
2004 b'parentnodes',
2003 b'{%s} ',
2005 b'{%s} ',
2004 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2006 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2005 )
2007 )
2006 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2008 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2007 meta = marker.metadata().copy()
2009 meta = marker.metadata().copy()
2008 meta.pop(b'date', None)
2010 meta.pop(b'date', None)
2009 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2011 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2010 fm.write(
2012 fm.write(
2011 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2013 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2012 )
2014 )
2013 fm.plain(b'\n')
2015 fm.plain(b'\n')
2014
2016
2015
2017
2016 def finddate(ui, repo, date):
2018 def finddate(ui, repo, date):
2017 """Find the tipmost changeset that matches the given date spec"""
2019 """Find the tipmost changeset that matches the given date spec"""
2018
2020
2019 df = dateutil.matchdate(date)
2021 df = dateutil.matchdate(date)
2020 m = scmutil.matchall(repo)
2022 m = scmutil.matchall(repo)
2021 results = {}
2023 results = {}
2022
2024
2023 def prep(ctx, fns):
2025 def prep(ctx, fns):
2024 d = ctx.date()
2026 d = ctx.date()
2025 if df(d[0]):
2027 if df(d[0]):
2026 results[ctx.rev()] = d
2028 results[ctx.rev()] = d
2027
2029
2028 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2030 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2029 rev = ctx.rev()
2031 rev = ctx.rev()
2030 if rev in results:
2032 if rev in results:
2031 ui.status(
2033 ui.status(
2032 _(b"found revision %d from %s\n")
2034 _(b"found revision %d from %s\n")
2033 % (rev, dateutil.datestr(results[rev]))
2035 % (rev, dateutil.datestr(results[rev]))
2034 )
2036 )
2035 return b'%d' % rev
2037 return b'%d' % rev
2036
2038
2037 raise error.Abort(_(b"revision matching date not found"))
2039 raise error.Abort(_(b"revision matching date not found"))
2038
2040
2039
2041
2040 def increasingwindows(windowsize=8, sizelimit=512):
2042 def increasingwindows(windowsize=8, sizelimit=512):
2041 while True:
2043 while True:
2042 yield windowsize
2044 yield windowsize
2043 if windowsize < sizelimit:
2045 if windowsize < sizelimit:
2044 windowsize *= 2
2046 windowsize *= 2
2045
2047
2046
2048
2047 def _walkrevs(repo, opts):
2049 def _walkrevs(repo, opts):
2048 # Default --rev value depends on --follow but --follow behavior
2050 # Default --rev value depends on --follow but --follow behavior
2049 # depends on revisions resolved from --rev...
2051 # depends on revisions resolved from --rev...
2050 follow = opts.get(b'follow') or opts.get(b'follow_first')
2052 follow = opts.get(b'follow') or opts.get(b'follow_first')
2051 if opts.get(b'rev'):
2053 if opts.get(b'rev'):
2052 revs = scmutil.revrange(repo, opts[b'rev'])
2054 revs = scmutil.revrange(repo, opts[b'rev'])
2053 elif follow and repo.dirstate.p1() == nullid:
2055 elif follow and repo.dirstate.p1() == nullid:
2054 revs = smartset.baseset()
2056 revs = smartset.baseset()
2055 elif follow:
2057 elif follow:
2056 revs = repo.revs(b'reverse(:.)')
2058 revs = repo.revs(b'reverse(:.)')
2057 else:
2059 else:
2058 revs = smartset.spanset(repo)
2060 revs = smartset.spanset(repo)
2059 revs.reverse()
2061 revs.reverse()
2060 return revs
2062 return revs
2061
2063
2062
2064
2063 class FileWalkError(Exception):
2065 class FileWalkError(Exception):
2064 pass
2066 pass
2065
2067
2066
2068
2067 def walkfilerevs(repo, match, follow, revs, fncache):
2069 def walkfilerevs(repo, match, follow, revs, fncache):
2068 '''Walks the file history for the matched files.
2070 '''Walks the file history for the matched files.
2069
2071
2070 Returns the changeset revs that are involved in the file history.
2072 Returns the changeset revs that are involved in the file history.
2071
2073
2072 Throws FileWalkError if the file history can't be walked using
2074 Throws FileWalkError if the file history can't be walked using
2073 filelogs alone.
2075 filelogs alone.
2074 '''
2076 '''
2075 wanted = set()
2077 wanted = set()
2076 copies = []
2078 copies = []
2077 minrev, maxrev = min(revs), max(revs)
2079 minrev, maxrev = min(revs), max(revs)
2078
2080
2079 def filerevs(filelog, last):
2081 def filerevs(filelog, last):
2080 """
2082 """
2081 Only files, no patterns. Check the history of each file.
2083 Only files, no patterns. Check the history of each file.
2082
2084
2083 Examines filelog entries within minrev, maxrev linkrev range
2085 Examines filelog entries within minrev, maxrev linkrev range
2084 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2086 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2085 tuples in backwards order
2087 tuples in backwards order
2086 """
2088 """
2087 cl_count = len(repo)
2089 cl_count = len(repo)
2088 revs = []
2090 revs = []
2089 for j in pycompat.xrange(0, last + 1):
2091 for j in pycompat.xrange(0, last + 1):
2090 linkrev = filelog.linkrev(j)
2092 linkrev = filelog.linkrev(j)
2091 if linkrev < minrev:
2093 if linkrev < minrev:
2092 continue
2094 continue
2093 # only yield rev for which we have the changelog, it can
2095 # only yield rev for which we have the changelog, it can
2094 # happen while doing "hg log" during a pull or commit
2096 # happen while doing "hg log" during a pull or commit
2095 if linkrev >= cl_count:
2097 if linkrev >= cl_count:
2096 break
2098 break
2097
2099
2098 parentlinkrevs = []
2100 parentlinkrevs = []
2099 for p in filelog.parentrevs(j):
2101 for p in filelog.parentrevs(j):
2100 if p != nullrev:
2102 if p != nullrev:
2101 parentlinkrevs.append(filelog.linkrev(p))
2103 parentlinkrevs.append(filelog.linkrev(p))
2102 n = filelog.node(j)
2104 n = filelog.node(j)
2103 revs.append(
2105 revs.append(
2104 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2106 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2105 )
2107 )
2106
2108
2107 return reversed(revs)
2109 return reversed(revs)
2108
2110
2109 def iterfiles():
2111 def iterfiles():
2110 pctx = repo[b'.']
2112 pctx = repo[b'.']
2111 for filename in match.files():
2113 for filename in match.files():
2112 if follow:
2114 if follow:
2113 if filename not in pctx:
2115 if filename not in pctx:
2114 raise error.Abort(
2116 raise error.Abort(
2115 _(
2117 _(
2116 b'cannot follow file not in parent '
2118 b'cannot follow file not in parent '
2117 b'revision: "%s"'
2119 b'revision: "%s"'
2118 )
2120 )
2119 % filename
2121 % filename
2120 )
2122 )
2121 yield filename, pctx[filename].filenode()
2123 yield filename, pctx[filename].filenode()
2122 else:
2124 else:
2123 yield filename, None
2125 yield filename, None
2124 for filename_node in copies:
2126 for filename_node in copies:
2125 yield filename_node
2127 yield filename_node
2126
2128
2127 for file_, node in iterfiles():
2129 for file_, node in iterfiles():
2128 filelog = repo.file(file_)
2130 filelog = repo.file(file_)
2129 if not len(filelog):
2131 if not len(filelog):
2130 if node is None:
2132 if node is None:
2131 # A zero count may be a directory or deleted file, so
2133 # A zero count may be a directory or deleted file, so
2132 # try to find matching entries on the slow path.
2134 # try to find matching entries on the slow path.
2133 if follow:
2135 if follow:
2134 raise error.Abort(
2136 raise error.Abort(
2135 _(b'cannot follow nonexistent file: "%s"') % file_
2137 _(b'cannot follow nonexistent file: "%s"') % file_
2136 )
2138 )
2137 raise FileWalkError(b"Cannot walk via filelog")
2139 raise FileWalkError(b"Cannot walk via filelog")
2138 else:
2140 else:
2139 continue
2141 continue
2140
2142
2141 if node is None:
2143 if node is None:
2142 last = len(filelog) - 1
2144 last = len(filelog) - 1
2143 else:
2145 else:
2144 last = filelog.rev(node)
2146 last = filelog.rev(node)
2145
2147
2146 # keep track of all ancestors of the file
2148 # keep track of all ancestors of the file
2147 ancestors = {filelog.linkrev(last)}
2149 ancestors = {filelog.linkrev(last)}
2148
2150
2149 # iterate from latest to oldest revision
2151 # iterate from latest to oldest revision
2150 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2152 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2151 if not follow:
2153 if not follow:
2152 if rev > maxrev:
2154 if rev > maxrev:
2153 continue
2155 continue
2154 else:
2156 else:
2155 # Note that last might not be the first interesting
2157 # Note that last might not be the first interesting
2156 # rev to us:
2158 # rev to us:
2157 # if the file has been changed after maxrev, we'll
2159 # if the file has been changed after maxrev, we'll
2158 # have linkrev(last) > maxrev, and we still need
2160 # have linkrev(last) > maxrev, and we still need
2159 # to explore the file graph
2161 # to explore the file graph
2160 if rev not in ancestors:
2162 if rev not in ancestors:
2161 continue
2163 continue
2162 # XXX insert 1327 fix here
2164 # XXX insert 1327 fix here
2163 if flparentlinkrevs:
2165 if flparentlinkrevs:
2164 ancestors.update(flparentlinkrevs)
2166 ancestors.update(flparentlinkrevs)
2165
2167
2166 fncache.setdefault(rev, []).append(file_)
2168 fncache.setdefault(rev, []).append(file_)
2167 wanted.add(rev)
2169 wanted.add(rev)
2168 if copied:
2170 if copied:
2169 copies.append(copied)
2171 copies.append(copied)
2170
2172
2171 return wanted
2173 return wanted
2172
2174
2173
2175
2174 class _followfilter(object):
2176 class _followfilter(object):
2175 def __init__(self, repo, onlyfirst=False):
2177 def __init__(self, repo, onlyfirst=False):
2176 self.repo = repo
2178 self.repo = repo
2177 self.startrev = nullrev
2179 self.startrev = nullrev
2178 self.roots = set()
2180 self.roots = set()
2179 self.onlyfirst = onlyfirst
2181 self.onlyfirst = onlyfirst
2180
2182
2181 def match(self, rev):
2183 def match(self, rev):
2182 def realparents(rev):
2184 def realparents(rev):
2183 if self.onlyfirst:
2185 if self.onlyfirst:
2184 return self.repo.changelog.parentrevs(rev)[0:1]
2186 return self.repo.changelog.parentrevs(rev)[0:1]
2185 else:
2187 else:
2186 return filter(
2188 return filter(
2187 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2189 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2188 )
2190 )
2189
2191
2190 if self.startrev == nullrev:
2192 if self.startrev == nullrev:
2191 self.startrev = rev
2193 self.startrev = rev
2192 return True
2194 return True
2193
2195
2194 if rev > self.startrev:
2196 if rev > self.startrev:
2195 # forward: all descendants
2197 # forward: all descendants
2196 if not self.roots:
2198 if not self.roots:
2197 self.roots.add(self.startrev)
2199 self.roots.add(self.startrev)
2198 for parent in realparents(rev):
2200 for parent in realparents(rev):
2199 if parent in self.roots:
2201 if parent in self.roots:
2200 self.roots.add(rev)
2202 self.roots.add(rev)
2201 return True
2203 return True
2202 else:
2204 else:
2203 # backwards: all parents
2205 # backwards: all parents
2204 if not self.roots:
2206 if not self.roots:
2205 self.roots.update(realparents(self.startrev))
2207 self.roots.update(realparents(self.startrev))
2206 if rev in self.roots:
2208 if rev in self.roots:
2207 self.roots.remove(rev)
2209 self.roots.remove(rev)
2208 self.roots.update(realparents(rev))
2210 self.roots.update(realparents(rev))
2209 return True
2211 return True
2210
2212
2211 return False
2213 return False
2212
2214
2213
2215
2214 def walkchangerevs(repo, match, opts, prepare):
2216 def walkchangerevs(repo, match, opts, prepare):
2215 '''Iterate over files and the revs in which they changed.
2217 '''Iterate over files and the revs in which they changed.
2216
2218
2217 Callers most commonly need to iterate backwards over the history
2219 Callers most commonly need to iterate backwards over the history
2218 in which they are interested. Doing so has awful (quadratic-looking)
2220 in which they are interested. Doing so has awful (quadratic-looking)
2219 performance, so we use iterators in a "windowed" way.
2221 performance, so we use iterators in a "windowed" way.
2220
2222
2221 We walk a window of revisions in the desired order. Within the
2223 We walk a window of revisions in the desired order. Within the
2222 window, we first walk forwards to gather data, then in the desired
2224 window, we first walk forwards to gather data, then in the desired
2223 order (usually backwards) to display it.
2225 order (usually backwards) to display it.
2224
2226
2225 This function returns an iterator yielding contexts. Before
2227 This function returns an iterator yielding contexts. Before
2226 yielding each context, the iterator will first call the prepare
2228 yielding each context, the iterator will first call the prepare
2227 function on each context in the window in forward order.'''
2229 function on each context in the window in forward order.'''
2228
2230
2229 allfiles = opts.get(b'all_files')
2231 allfiles = opts.get(b'all_files')
2230 follow = opts.get(b'follow') or opts.get(b'follow_first')
2232 follow = opts.get(b'follow') or opts.get(b'follow_first')
2231 revs = _walkrevs(repo, opts)
2233 revs = _walkrevs(repo, opts)
2232 if not revs:
2234 if not revs:
2233 return []
2235 return []
2234 wanted = set()
2236 wanted = set()
2235 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2237 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2236 fncache = {}
2238 fncache = {}
2237 change = repo.__getitem__
2239 change = repo.__getitem__
2238
2240
2239 # First step is to fill wanted, the set of revisions that we want to yield.
2241 # First step is to fill wanted, the set of revisions that we want to yield.
2240 # When it does not induce extra cost, we also fill fncache for revisions in
2242 # When it does not induce extra cost, we also fill fncache for revisions in
2241 # wanted: a cache of filenames that were changed (ctx.files()) and that
2243 # wanted: a cache of filenames that were changed (ctx.files()) and that
2242 # match the file filtering conditions.
2244 # match the file filtering conditions.
2243
2245
2244 if match.always() or allfiles:
2246 if match.always() or allfiles:
2245 # No files, no patterns. Display all revs.
2247 # No files, no patterns. Display all revs.
2246 wanted = revs
2248 wanted = revs
2247 elif not slowpath:
2249 elif not slowpath:
2248 # We only have to read through the filelog to find wanted revisions
2250 # We only have to read through the filelog to find wanted revisions
2249
2251
2250 try:
2252 try:
2251 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2253 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2252 except FileWalkError:
2254 except FileWalkError:
2253 slowpath = True
2255 slowpath = True
2254
2256
2255 # We decided to fall back to the slowpath because at least one
2257 # We decided to fall back to the slowpath because at least one
2256 # of the paths was not a file. Check to see if at least one of them
2258 # of the paths was not a file. Check to see if at least one of them
2257 # existed in history, otherwise simply return
2259 # existed in history, otherwise simply return
2258 for path in match.files():
2260 for path in match.files():
2259 if path == b'.' or path in repo.store:
2261 if path == b'.' or path in repo.store:
2260 break
2262 break
2261 else:
2263 else:
2262 return []
2264 return []
2263
2265
2264 if slowpath:
2266 if slowpath:
2265 # We have to read the changelog to match filenames against
2267 # We have to read the changelog to match filenames against
2266 # changed files
2268 # changed files
2267
2269
2268 if follow:
2270 if follow:
2269 raise error.Abort(
2271 raise error.Abort(
2270 _(b'can only follow copies/renames for explicit filenames')
2272 _(b'can only follow copies/renames for explicit filenames')
2271 )
2273 )
2272
2274
2273 # The slow path checks files modified in every changeset.
2275 # The slow path checks files modified in every changeset.
2274 # This is really slow on large repos, so compute the set lazily.
2276 # This is really slow on large repos, so compute the set lazily.
2275 class lazywantedset(object):
2277 class lazywantedset(object):
2276 def __init__(self):
2278 def __init__(self):
2277 self.set = set()
2279 self.set = set()
2278 self.revs = set(revs)
2280 self.revs = set(revs)
2279
2281
2280 # No need to worry about locality here because it will be accessed
2282 # No need to worry about locality here because it will be accessed
2281 # in the same order as the increasing window below.
2283 # in the same order as the increasing window below.
2282 def __contains__(self, value):
2284 def __contains__(self, value):
2283 if value in self.set:
2285 if value in self.set:
2284 return True
2286 return True
2285 elif not value in self.revs:
2287 elif not value in self.revs:
2286 return False
2288 return False
2287 else:
2289 else:
2288 self.revs.discard(value)
2290 self.revs.discard(value)
2289 ctx = change(value)
2291 ctx = change(value)
2290 if allfiles:
2292 if allfiles:
2291 matches = list(ctx.manifest().walk(match))
2293 matches = list(ctx.manifest().walk(match))
2292 else:
2294 else:
2293 matches = [f for f in ctx.files() if match(f)]
2295 matches = [f for f in ctx.files() if match(f)]
2294 if matches:
2296 if matches:
2295 fncache[value] = matches
2297 fncache[value] = matches
2296 self.set.add(value)
2298 self.set.add(value)
2297 return True
2299 return True
2298 return False
2300 return False
2299
2301
2300 def discard(self, value):
2302 def discard(self, value):
2301 self.revs.discard(value)
2303 self.revs.discard(value)
2302 self.set.discard(value)
2304 self.set.discard(value)
2303
2305
2304 wanted = lazywantedset()
2306 wanted = lazywantedset()
2305
2307
2306 # it might be worthwhile to do this in the iterator if the rev range
2308 # it might be worthwhile to do this in the iterator if the rev range
2307 # is descending and the prune args are all within that range
2309 # is descending and the prune args are all within that range
2308 for rev in opts.get(b'prune', ()):
2310 for rev in opts.get(b'prune', ()):
2309 rev = repo[rev].rev()
2311 rev = repo[rev].rev()
2310 ff = _followfilter(repo)
2312 ff = _followfilter(repo)
2311 stop = min(revs[0], revs[-1])
2313 stop = min(revs[0], revs[-1])
2312 for x in pycompat.xrange(rev, stop - 1, -1):
2314 for x in pycompat.xrange(rev, stop - 1, -1):
2313 if ff.match(x):
2315 if ff.match(x):
2314 wanted = wanted - [x]
2316 wanted = wanted - [x]
2315
2317
2316 # Now that wanted is correctly initialized, we can iterate over the
2318 # Now that wanted is correctly initialized, we can iterate over the
2317 # revision range, yielding only revisions in wanted.
2319 # revision range, yielding only revisions in wanted.
2318 def iterate():
2320 def iterate():
2319 if follow and match.always():
2321 if follow and match.always():
2320 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2322 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2321
2323
2322 def want(rev):
2324 def want(rev):
2323 return ff.match(rev) and rev in wanted
2325 return ff.match(rev) and rev in wanted
2324
2326
2325 else:
2327 else:
2326
2328
2327 def want(rev):
2329 def want(rev):
2328 return rev in wanted
2330 return rev in wanted
2329
2331
2330 it = iter(revs)
2332 it = iter(revs)
2331 stopiteration = False
2333 stopiteration = False
2332 for windowsize in increasingwindows():
2334 for windowsize in increasingwindows():
2333 nrevs = []
2335 nrevs = []
2334 for i in pycompat.xrange(windowsize):
2336 for i in pycompat.xrange(windowsize):
2335 rev = next(it, None)
2337 rev = next(it, None)
2336 if rev is None:
2338 if rev is None:
2337 stopiteration = True
2339 stopiteration = True
2338 break
2340 break
2339 elif want(rev):
2341 elif want(rev):
2340 nrevs.append(rev)
2342 nrevs.append(rev)
2341 for rev in sorted(nrevs):
2343 for rev in sorted(nrevs):
2342 fns = fncache.get(rev)
2344 fns = fncache.get(rev)
2343 ctx = change(rev)
2345 ctx = change(rev)
2344 if not fns:
2346 if not fns:
2345
2347
2346 def fns_generator():
2348 def fns_generator():
2347 if allfiles:
2349 if allfiles:
2348 fiter = iter(ctx)
2350 fiter = iter(ctx)
2349 else:
2351 else:
2350 fiter = ctx.files()
2352 fiter = ctx.files()
2351 for f in fiter:
2353 for f in fiter:
2352 if match(f):
2354 if match(f):
2353 yield f
2355 yield f
2354
2356
2355 fns = fns_generator()
2357 fns = fns_generator()
2356 prepare(ctx, fns)
2358 prepare(ctx, fns)
2357 for rev in nrevs:
2359 for rev in nrevs:
2358 yield change(rev)
2360 yield change(rev)
2359
2361
2360 if stopiteration:
2362 if stopiteration:
2361 break
2363 break
2362
2364
2363 return iterate()
2365 return iterate()
2364
2366
2365
2367
2366 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2368 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2367 bad = []
2369 bad = []
2368
2370
2369 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2371 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2370 names = []
2372 names = []
2371 wctx = repo[None]
2373 wctx = repo[None]
2372 cca = None
2374 cca = None
2373 abort, warn = scmutil.checkportabilityalert(ui)
2375 abort, warn = scmutil.checkportabilityalert(ui)
2374 if abort or warn:
2376 if abort or warn:
2375 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2377 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2376
2378
2377 match = repo.narrowmatch(match, includeexact=True)
2379 match = repo.narrowmatch(match, includeexact=True)
2378 badmatch = matchmod.badmatch(match, badfn)
2380 badmatch = matchmod.badmatch(match, badfn)
2379 dirstate = repo.dirstate
2381 dirstate = repo.dirstate
2380 # We don't want to just call wctx.walk here, since it would return a lot of
2382 # We don't want to just call wctx.walk here, since it would return a lot of
2381 # clean files, which we aren't interested in and takes time.
2383 # clean files, which we aren't interested in and takes time.
2382 for f in sorted(
2384 for f in sorted(
2383 dirstate.walk(
2385 dirstate.walk(
2384 badmatch,
2386 badmatch,
2385 subrepos=sorted(wctx.substate),
2387 subrepos=sorted(wctx.substate),
2386 unknown=True,
2388 unknown=True,
2387 ignored=False,
2389 ignored=False,
2388 full=False,
2390 full=False,
2389 )
2391 )
2390 ):
2392 ):
2391 exact = match.exact(f)
2393 exact = match.exact(f)
2392 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2394 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2393 if cca:
2395 if cca:
2394 cca(f)
2396 cca(f)
2395 names.append(f)
2397 names.append(f)
2396 if ui.verbose or not exact:
2398 if ui.verbose or not exact:
2397 ui.status(
2399 ui.status(
2398 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2400 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2399 )
2401 )
2400
2402
2401 for subpath in sorted(wctx.substate):
2403 for subpath in sorted(wctx.substate):
2402 sub = wctx.sub(subpath)
2404 sub = wctx.sub(subpath)
2403 try:
2405 try:
2404 submatch = matchmod.subdirmatcher(subpath, match)
2406 submatch = matchmod.subdirmatcher(subpath, match)
2405 subprefix = repo.wvfs.reljoin(prefix, subpath)
2407 subprefix = repo.wvfs.reljoin(prefix, subpath)
2406 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2408 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2407 if opts.get('subrepos'):
2409 if opts.get('subrepos'):
2408 bad.extend(
2410 bad.extend(
2409 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2411 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2410 )
2412 )
2411 else:
2413 else:
2412 bad.extend(
2414 bad.extend(
2413 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2415 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2414 )
2416 )
2415 except error.LookupError:
2417 except error.LookupError:
2416 ui.status(
2418 ui.status(
2417 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2419 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2418 )
2420 )
2419
2421
2420 if not opts.get('dry_run'):
2422 if not opts.get('dry_run'):
2421 rejected = wctx.add(names, prefix)
2423 rejected = wctx.add(names, prefix)
2422 bad.extend(f for f in rejected if f in match.files())
2424 bad.extend(f for f in rejected if f in match.files())
2423 return bad
2425 return bad
2424
2426
2425
2427
2426 def addwebdirpath(repo, serverpath, webconf):
2428 def addwebdirpath(repo, serverpath, webconf):
2427 webconf[serverpath] = repo.root
2429 webconf[serverpath] = repo.root
2428 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2430 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2429
2431
2430 for r in repo.revs(b'filelog("path:.hgsub")'):
2432 for r in repo.revs(b'filelog("path:.hgsub")'):
2431 ctx = repo[r]
2433 ctx = repo[r]
2432 for subpath in ctx.substate:
2434 for subpath in ctx.substate:
2433 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2435 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2434
2436
2435
2437
2436 def forget(
2438 def forget(
2437 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2439 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2438 ):
2440 ):
2439 if dryrun and interactive:
2441 if dryrun and interactive:
2440 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2442 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2441 bad = []
2443 bad = []
2442 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2444 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2443 wctx = repo[None]
2445 wctx = repo[None]
2444 forgot = []
2446 forgot = []
2445
2447
2446 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2448 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2447 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2449 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2448 if explicitonly:
2450 if explicitonly:
2449 forget = [f for f in forget if match.exact(f)]
2451 forget = [f for f in forget if match.exact(f)]
2450
2452
2451 for subpath in sorted(wctx.substate):
2453 for subpath in sorted(wctx.substate):
2452 sub = wctx.sub(subpath)
2454 sub = wctx.sub(subpath)
2453 submatch = matchmod.subdirmatcher(subpath, match)
2455 submatch = matchmod.subdirmatcher(subpath, match)
2454 subprefix = repo.wvfs.reljoin(prefix, subpath)
2456 subprefix = repo.wvfs.reljoin(prefix, subpath)
2455 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2457 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2456 try:
2458 try:
2457 subbad, subforgot = sub.forget(
2459 subbad, subforgot = sub.forget(
2458 submatch,
2460 submatch,
2459 subprefix,
2461 subprefix,
2460 subuipathfn,
2462 subuipathfn,
2461 dryrun=dryrun,
2463 dryrun=dryrun,
2462 interactive=interactive,
2464 interactive=interactive,
2463 )
2465 )
2464 bad.extend([subpath + b'/' + f for f in subbad])
2466 bad.extend([subpath + b'/' + f for f in subbad])
2465 forgot.extend([subpath + b'/' + f for f in subforgot])
2467 forgot.extend([subpath + b'/' + f for f in subforgot])
2466 except error.LookupError:
2468 except error.LookupError:
2467 ui.status(
2469 ui.status(
2468 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2470 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2469 )
2471 )
2470
2472
2471 if not explicitonly:
2473 if not explicitonly:
2472 for f in match.files():
2474 for f in match.files():
2473 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2475 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2474 if f not in forgot:
2476 if f not in forgot:
2475 if repo.wvfs.exists(f):
2477 if repo.wvfs.exists(f):
2476 # Don't complain if the exact case match wasn't given.
2478 # Don't complain if the exact case match wasn't given.
2477 # But don't do this until after checking 'forgot', so
2479 # But don't do this until after checking 'forgot', so
2478 # that subrepo files aren't normalized, and this op is
2480 # that subrepo files aren't normalized, and this op is
2479 # purely from data cached by the status walk above.
2481 # purely from data cached by the status walk above.
2480 if repo.dirstate.normalize(f) in repo.dirstate:
2482 if repo.dirstate.normalize(f) in repo.dirstate:
2481 continue
2483 continue
2482 ui.warn(
2484 ui.warn(
2483 _(
2485 _(
2484 b'not removing %s: '
2486 b'not removing %s: '
2485 b'file is already untracked\n'
2487 b'file is already untracked\n'
2486 )
2488 )
2487 % uipathfn(f)
2489 % uipathfn(f)
2488 )
2490 )
2489 bad.append(f)
2491 bad.append(f)
2490
2492
2491 if interactive:
2493 if interactive:
2492 responses = _(
2494 responses = _(
2493 b'[Ynsa?]'
2495 b'[Ynsa?]'
2494 b'$$ &Yes, forget this file'
2496 b'$$ &Yes, forget this file'
2495 b'$$ &No, skip this file'
2497 b'$$ &No, skip this file'
2496 b'$$ &Skip remaining files'
2498 b'$$ &Skip remaining files'
2497 b'$$ Include &all remaining files'
2499 b'$$ Include &all remaining files'
2498 b'$$ &? (display help)'
2500 b'$$ &? (display help)'
2499 )
2501 )
2500 for filename in forget[:]:
2502 for filename in forget[:]:
2501 r = ui.promptchoice(
2503 r = ui.promptchoice(
2502 _(b'forget %s %s') % (uipathfn(filename), responses)
2504 _(b'forget %s %s') % (uipathfn(filename), responses)
2503 )
2505 )
2504 if r == 4: # ?
2506 if r == 4: # ?
2505 while r == 4:
2507 while r == 4:
2506 for c, t in ui.extractchoices(responses)[1]:
2508 for c, t in ui.extractchoices(responses)[1]:
2507 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2509 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2508 r = ui.promptchoice(
2510 r = ui.promptchoice(
2509 _(b'forget %s %s') % (uipathfn(filename), responses)
2511 _(b'forget %s %s') % (uipathfn(filename), responses)
2510 )
2512 )
2511 if r == 0: # yes
2513 if r == 0: # yes
2512 continue
2514 continue
2513 elif r == 1: # no
2515 elif r == 1: # no
2514 forget.remove(filename)
2516 forget.remove(filename)
2515 elif r == 2: # Skip
2517 elif r == 2: # Skip
2516 fnindex = forget.index(filename)
2518 fnindex = forget.index(filename)
2517 del forget[fnindex:]
2519 del forget[fnindex:]
2518 break
2520 break
2519 elif r == 3: # All
2521 elif r == 3: # All
2520 break
2522 break
2521
2523
2522 for f in forget:
2524 for f in forget:
2523 if ui.verbose or not match.exact(f) or interactive:
2525 if ui.verbose or not match.exact(f) or interactive:
2524 ui.status(
2526 ui.status(
2525 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2527 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2526 )
2528 )
2527
2529
2528 if not dryrun:
2530 if not dryrun:
2529 rejected = wctx.forget(forget, prefix)
2531 rejected = wctx.forget(forget, prefix)
2530 bad.extend(f for f in rejected if f in match.files())
2532 bad.extend(f for f in rejected if f in match.files())
2531 forgot.extend(f for f in forget if f not in rejected)
2533 forgot.extend(f for f in forget if f not in rejected)
2532 return bad, forgot
2534 return bad, forgot
2533
2535
2534
2536
2535 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2537 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2536 ret = 1
2538 ret = 1
2537
2539
2538 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2540 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2539 for f in ctx.matches(m):
2541 for f in ctx.matches(m):
2540 fm.startitem()
2542 fm.startitem()
2541 fm.context(ctx=ctx)
2543 fm.context(ctx=ctx)
2542 if needsfctx:
2544 if needsfctx:
2543 fc = ctx[f]
2545 fc = ctx[f]
2544 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2546 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2545 fm.data(path=f)
2547 fm.data(path=f)
2546 fm.plain(fmt % uipathfn(f))
2548 fm.plain(fmt % uipathfn(f))
2547 ret = 0
2549 ret = 0
2548
2550
2549 for subpath in sorted(ctx.substate):
2551 for subpath in sorted(ctx.substate):
2550 submatch = matchmod.subdirmatcher(subpath, m)
2552 submatch = matchmod.subdirmatcher(subpath, m)
2551 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2553 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2552 if subrepos or m.exact(subpath) or any(submatch.files()):
2554 if subrepos or m.exact(subpath) or any(submatch.files()):
2553 sub = ctx.sub(subpath)
2555 sub = ctx.sub(subpath)
2554 try:
2556 try:
2555 recurse = m.exact(subpath) or subrepos
2557 recurse = m.exact(subpath) or subrepos
2556 if (
2558 if (
2557 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2559 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2558 == 0
2560 == 0
2559 ):
2561 ):
2560 ret = 0
2562 ret = 0
2561 except error.LookupError:
2563 except error.LookupError:
2562 ui.status(
2564 ui.status(
2563 _(b"skipping missing subrepository: %s\n")
2565 _(b"skipping missing subrepository: %s\n")
2564 % uipathfn(subpath)
2566 % uipathfn(subpath)
2565 )
2567 )
2566
2568
2567 return ret
2569 return ret
2568
2570
2569
2571
2570 def remove(
2572 def remove(
2571 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2573 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2572 ):
2574 ):
2573 ret = 0
2575 ret = 0
2574 s = repo.status(match=m, clean=True)
2576 s = repo.status(match=m, clean=True)
2575 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2577 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2576
2578
2577 wctx = repo[None]
2579 wctx = repo[None]
2578
2580
2579 if warnings is None:
2581 if warnings is None:
2580 warnings = []
2582 warnings = []
2581 warn = True
2583 warn = True
2582 else:
2584 else:
2583 warn = False
2585 warn = False
2584
2586
2585 subs = sorted(wctx.substate)
2587 subs = sorted(wctx.substate)
2586 progress = ui.makeprogress(
2588 progress = ui.makeprogress(
2587 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2589 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2588 )
2590 )
2589 for subpath in subs:
2591 for subpath in subs:
2590 submatch = matchmod.subdirmatcher(subpath, m)
2592 submatch = matchmod.subdirmatcher(subpath, m)
2591 subprefix = repo.wvfs.reljoin(prefix, subpath)
2593 subprefix = repo.wvfs.reljoin(prefix, subpath)
2592 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2594 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2593 if subrepos or m.exact(subpath) or any(submatch.files()):
2595 if subrepos or m.exact(subpath) or any(submatch.files()):
2594 progress.increment()
2596 progress.increment()
2595 sub = wctx.sub(subpath)
2597 sub = wctx.sub(subpath)
2596 try:
2598 try:
2597 if sub.removefiles(
2599 if sub.removefiles(
2598 submatch,
2600 submatch,
2599 subprefix,
2601 subprefix,
2600 subuipathfn,
2602 subuipathfn,
2601 after,
2603 after,
2602 force,
2604 force,
2603 subrepos,
2605 subrepos,
2604 dryrun,
2606 dryrun,
2605 warnings,
2607 warnings,
2606 ):
2608 ):
2607 ret = 1
2609 ret = 1
2608 except error.LookupError:
2610 except error.LookupError:
2609 warnings.append(
2611 warnings.append(
2610 _(b"skipping missing subrepository: %s\n")
2612 _(b"skipping missing subrepository: %s\n")
2611 % uipathfn(subpath)
2613 % uipathfn(subpath)
2612 )
2614 )
2613 progress.complete()
2615 progress.complete()
2614
2616
2615 # warn about failure to delete explicit files/dirs
2617 # warn about failure to delete explicit files/dirs
2616 deleteddirs = pathutil.dirs(deleted)
2618 deleteddirs = pathutil.dirs(deleted)
2617 files = m.files()
2619 files = m.files()
2618 progress = ui.makeprogress(
2620 progress = ui.makeprogress(
2619 _(b'deleting'), total=len(files), unit=_(b'files')
2621 _(b'deleting'), total=len(files), unit=_(b'files')
2620 )
2622 )
2621 for f in files:
2623 for f in files:
2622
2624
2623 def insubrepo():
2625 def insubrepo():
2624 for subpath in wctx.substate:
2626 for subpath in wctx.substate:
2625 if f.startswith(subpath + b'/'):
2627 if f.startswith(subpath + b'/'):
2626 return True
2628 return True
2627 return False
2629 return False
2628
2630
2629 progress.increment()
2631 progress.increment()
2630 isdir = f in deleteddirs or wctx.hasdir(f)
2632 isdir = f in deleteddirs or wctx.hasdir(f)
2631 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2633 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2632 continue
2634 continue
2633
2635
2634 if repo.wvfs.exists(f):
2636 if repo.wvfs.exists(f):
2635 if repo.wvfs.isdir(f):
2637 if repo.wvfs.isdir(f):
2636 warnings.append(
2638 warnings.append(
2637 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2639 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2638 )
2640 )
2639 else:
2641 else:
2640 warnings.append(
2642 warnings.append(
2641 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2643 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2642 )
2644 )
2643 # missing files will generate a warning elsewhere
2645 # missing files will generate a warning elsewhere
2644 ret = 1
2646 ret = 1
2645 progress.complete()
2647 progress.complete()
2646
2648
2647 if force:
2649 if force:
2648 list = modified + deleted + clean + added
2650 list = modified + deleted + clean + added
2649 elif after:
2651 elif after:
2650 list = deleted
2652 list = deleted
2651 remaining = modified + added + clean
2653 remaining = modified + added + clean
2652 progress = ui.makeprogress(
2654 progress = ui.makeprogress(
2653 _(b'skipping'), total=len(remaining), unit=_(b'files')
2655 _(b'skipping'), total=len(remaining), unit=_(b'files')
2654 )
2656 )
2655 for f in remaining:
2657 for f in remaining:
2656 progress.increment()
2658 progress.increment()
2657 if ui.verbose or (f in files):
2659 if ui.verbose or (f in files):
2658 warnings.append(
2660 warnings.append(
2659 _(b'not removing %s: file still exists\n') % uipathfn(f)
2661 _(b'not removing %s: file still exists\n') % uipathfn(f)
2660 )
2662 )
2661 ret = 1
2663 ret = 1
2662 progress.complete()
2664 progress.complete()
2663 else:
2665 else:
2664 list = deleted + clean
2666 list = deleted + clean
2665 progress = ui.makeprogress(
2667 progress = ui.makeprogress(
2666 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2668 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2667 )
2669 )
2668 for f in modified:
2670 for f in modified:
2669 progress.increment()
2671 progress.increment()
2670 warnings.append(
2672 warnings.append(
2671 _(
2673 _(
2672 b'not removing %s: file is modified (use -f'
2674 b'not removing %s: file is modified (use -f'
2673 b' to force removal)\n'
2675 b' to force removal)\n'
2674 )
2676 )
2675 % uipathfn(f)
2677 % uipathfn(f)
2676 )
2678 )
2677 ret = 1
2679 ret = 1
2678 for f in added:
2680 for f in added:
2679 progress.increment()
2681 progress.increment()
2680 warnings.append(
2682 warnings.append(
2681 _(
2683 _(
2682 b"not removing %s: file has been marked for add"
2684 b"not removing %s: file has been marked for add"
2683 b" (use 'hg forget' to undo add)\n"
2685 b" (use 'hg forget' to undo add)\n"
2684 )
2686 )
2685 % uipathfn(f)
2687 % uipathfn(f)
2686 )
2688 )
2687 ret = 1
2689 ret = 1
2688 progress.complete()
2690 progress.complete()
2689
2691
2690 list = sorted(list)
2692 list = sorted(list)
2691 progress = ui.makeprogress(
2693 progress = ui.makeprogress(
2692 _(b'deleting'), total=len(list), unit=_(b'files')
2694 _(b'deleting'), total=len(list), unit=_(b'files')
2693 )
2695 )
2694 for f in list:
2696 for f in list:
2695 if ui.verbose or not m.exact(f):
2697 if ui.verbose or not m.exact(f):
2696 progress.increment()
2698 progress.increment()
2697 ui.status(
2699 ui.status(
2698 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2700 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2699 )
2701 )
2700 progress.complete()
2702 progress.complete()
2701
2703
2702 if not dryrun:
2704 if not dryrun:
2703 with repo.wlock():
2705 with repo.wlock():
2704 if not after:
2706 if not after:
2705 for f in list:
2707 for f in list:
2706 if f in added:
2708 if f in added:
2707 continue # we never unlink added files on remove
2709 continue # we never unlink added files on remove
2708 rmdir = repo.ui.configbool(
2710 rmdir = repo.ui.configbool(
2709 b'experimental', b'removeemptydirs'
2711 b'experimental', b'removeemptydirs'
2710 )
2712 )
2711 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2713 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2712 repo[None].forget(list)
2714 repo[None].forget(list)
2713
2715
2714 if warn:
2716 if warn:
2715 for warning in warnings:
2717 for warning in warnings:
2716 ui.warn(warning)
2718 ui.warn(warning)
2717
2719
2718 return ret
2720 return ret
2719
2721
2720
2722
2721 def _catfmtneedsdata(fm):
2723 def _catfmtneedsdata(fm):
2722 return not fm.datahint() or b'data' in fm.datahint()
2724 return not fm.datahint() or b'data' in fm.datahint()
2723
2725
2724
2726
2725 def _updatecatformatter(fm, ctx, matcher, path, decode):
2727 def _updatecatformatter(fm, ctx, matcher, path, decode):
2726 """Hook for adding data to the formatter used by ``hg cat``.
2728 """Hook for adding data to the formatter used by ``hg cat``.
2727
2729
2728 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2730 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2729 this method first."""
2731 this method first."""
2730
2732
2731 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2733 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2732 # wasn't requested.
2734 # wasn't requested.
2733 data = b''
2735 data = b''
2734 if _catfmtneedsdata(fm):
2736 if _catfmtneedsdata(fm):
2735 data = ctx[path].data()
2737 data = ctx[path].data()
2736 if decode:
2738 if decode:
2737 data = ctx.repo().wwritedata(path, data)
2739 data = ctx.repo().wwritedata(path, data)
2738 fm.startitem()
2740 fm.startitem()
2739 fm.context(ctx=ctx)
2741 fm.context(ctx=ctx)
2740 fm.write(b'data', b'%s', data)
2742 fm.write(b'data', b'%s', data)
2741 fm.data(path=path)
2743 fm.data(path=path)
2742
2744
2743
2745
2744 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2746 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2745 err = 1
2747 err = 1
2746 opts = pycompat.byteskwargs(opts)
2748 opts = pycompat.byteskwargs(opts)
2747
2749
2748 def write(path):
2750 def write(path):
2749 filename = None
2751 filename = None
2750 if fntemplate:
2752 if fntemplate:
2751 filename = makefilename(
2753 filename = makefilename(
2752 ctx, fntemplate, pathname=os.path.join(prefix, path)
2754 ctx, fntemplate, pathname=os.path.join(prefix, path)
2753 )
2755 )
2754 # attempt to create the directory if it does not already exist
2756 # attempt to create the directory if it does not already exist
2755 try:
2757 try:
2756 os.makedirs(os.path.dirname(filename))
2758 os.makedirs(os.path.dirname(filename))
2757 except OSError:
2759 except OSError:
2758 pass
2760 pass
2759 with formatter.maybereopen(basefm, filename) as fm:
2761 with formatter.maybereopen(basefm, filename) as fm:
2760 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2762 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2761
2763
2762 # Automation often uses hg cat on single files, so special case it
2764 # Automation often uses hg cat on single files, so special case it
2763 # for performance to avoid the cost of parsing the manifest.
2765 # for performance to avoid the cost of parsing the manifest.
2764 if len(matcher.files()) == 1 and not matcher.anypats():
2766 if len(matcher.files()) == 1 and not matcher.anypats():
2765 file = matcher.files()[0]
2767 file = matcher.files()[0]
2766 mfl = repo.manifestlog
2768 mfl = repo.manifestlog
2767 mfnode = ctx.manifestnode()
2769 mfnode = ctx.manifestnode()
2768 try:
2770 try:
2769 if mfnode and mfl[mfnode].find(file)[0]:
2771 if mfnode and mfl[mfnode].find(file)[0]:
2770 if _catfmtneedsdata(basefm):
2772 if _catfmtneedsdata(basefm):
2771 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2773 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2772 write(file)
2774 write(file)
2773 return 0
2775 return 0
2774 except KeyError:
2776 except KeyError:
2775 pass
2777 pass
2776
2778
2777 if _catfmtneedsdata(basefm):
2779 if _catfmtneedsdata(basefm):
2778 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2780 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2779
2781
2780 for abs in ctx.walk(matcher):
2782 for abs in ctx.walk(matcher):
2781 write(abs)
2783 write(abs)
2782 err = 0
2784 err = 0
2783
2785
2784 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2786 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2785 for subpath in sorted(ctx.substate):
2787 for subpath in sorted(ctx.substate):
2786 sub = ctx.sub(subpath)
2788 sub = ctx.sub(subpath)
2787 try:
2789 try:
2788 submatch = matchmod.subdirmatcher(subpath, matcher)
2790 submatch = matchmod.subdirmatcher(subpath, matcher)
2789 subprefix = os.path.join(prefix, subpath)
2791 subprefix = os.path.join(prefix, subpath)
2790 if not sub.cat(
2792 if not sub.cat(
2791 submatch,
2793 submatch,
2792 basefm,
2794 basefm,
2793 fntemplate,
2795 fntemplate,
2794 subprefix,
2796 subprefix,
2795 **pycompat.strkwargs(opts)
2797 **pycompat.strkwargs(opts)
2796 ):
2798 ):
2797 err = 0
2799 err = 0
2798 except error.RepoLookupError:
2800 except error.RepoLookupError:
2799 ui.status(
2801 ui.status(
2800 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2802 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2801 )
2803 )
2802
2804
2803 return err
2805 return err
2804
2806
2805
2807
2806 def commit(ui, repo, commitfunc, pats, opts):
2808 def commit(ui, repo, commitfunc, pats, opts):
2807 '''commit the specified files or all outstanding changes'''
2809 '''commit the specified files or all outstanding changes'''
2808 date = opts.get(b'date')
2810 date = opts.get(b'date')
2809 if date:
2811 if date:
2810 opts[b'date'] = dateutil.parsedate(date)
2812 opts[b'date'] = dateutil.parsedate(date)
2811 message = logmessage(ui, opts)
2813 message = logmessage(ui, opts)
2812 matcher = scmutil.match(repo[None], pats, opts)
2814 matcher = scmutil.match(repo[None], pats, opts)
2813
2815
2814 dsguard = None
2816 dsguard = None
2815 # extract addremove carefully -- this function can be called from a command
2817 # extract addremove carefully -- this function can be called from a command
2816 # that doesn't support addremove
2818 # that doesn't support addremove
2817 if opts.get(b'addremove'):
2819 if opts.get(b'addremove'):
2818 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2820 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2819 with dsguard or util.nullcontextmanager():
2821 with dsguard or util.nullcontextmanager():
2820 if dsguard:
2822 if dsguard:
2821 relative = scmutil.anypats(pats, opts)
2823 relative = scmutil.anypats(pats, opts)
2822 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2824 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2823 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2825 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2824 raise error.Abort(
2826 raise error.Abort(
2825 _(b"failed to mark all new/missing files as added/removed")
2827 _(b"failed to mark all new/missing files as added/removed")
2826 )
2828 )
2827
2829
2828 return commitfunc(ui, repo, message, matcher, opts)
2830 return commitfunc(ui, repo, message, matcher, opts)
2829
2831
2830
2832
2831 def samefile(f, ctx1, ctx2):
2833 def samefile(f, ctx1, ctx2):
2832 if f in ctx1.manifest():
2834 if f in ctx1.manifest():
2833 a = ctx1.filectx(f)
2835 a = ctx1.filectx(f)
2834 if f in ctx2.manifest():
2836 if f in ctx2.manifest():
2835 b = ctx2.filectx(f)
2837 b = ctx2.filectx(f)
2836 return not a.cmp(b) and a.flags() == b.flags()
2838 return not a.cmp(b) and a.flags() == b.flags()
2837 else:
2839 else:
2838 return False
2840 return False
2839 else:
2841 else:
2840 return f not in ctx2.manifest()
2842 return f not in ctx2.manifest()
2841
2843
2842
2844
2843 def amend(ui, repo, old, extra, pats, opts):
2845 def amend(ui, repo, old, extra, pats, opts):
2844 # avoid cycle context -> subrepo -> cmdutil
2846 # avoid cycle context -> subrepo -> cmdutil
2845 from . import context
2847 from . import context
2846
2848
2847 # amend will reuse the existing user if not specified, but the obsolete
2849 # amend will reuse the existing user if not specified, but the obsolete
2848 # marker creation requires that the current user's name is specified.
2850 # marker creation requires that the current user's name is specified.
2849 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2851 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2850 ui.username() # raise exception if username not set
2852 ui.username() # raise exception if username not set
2851
2853
2852 ui.note(_(b'amending changeset %s\n') % old)
2854 ui.note(_(b'amending changeset %s\n') % old)
2853 base = old.p1()
2855 base = old.p1()
2854
2856
2855 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2857 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2856 # Participating changesets:
2858 # Participating changesets:
2857 #
2859 #
2858 # wctx o - workingctx that contains changes from working copy
2860 # wctx o - workingctx that contains changes from working copy
2859 # | to go into amending commit
2861 # | to go into amending commit
2860 # |
2862 # |
2861 # old o - changeset to amend
2863 # old o - changeset to amend
2862 # |
2864 # |
2863 # base o - first parent of the changeset to amend
2865 # base o - first parent of the changeset to amend
2864 wctx = repo[None]
2866 wctx = repo[None]
2865
2867
2866 # Copy to avoid mutating input
2868 # Copy to avoid mutating input
2867 extra = extra.copy()
2869 extra = extra.copy()
2868 # Update extra dict from amended commit (e.g. to preserve graft
2870 # Update extra dict from amended commit (e.g. to preserve graft
2869 # source)
2871 # source)
2870 extra.update(old.extra())
2872 extra.update(old.extra())
2871
2873
2872 # Also update it from the from the wctx
2874 # Also update it from the from the wctx
2873 extra.update(wctx.extra())
2875 extra.update(wctx.extra())
2874
2876
2875 # date-only change should be ignored?
2877 # date-only change should be ignored?
2876 datemaydiffer = resolvecommitoptions(ui, opts)
2878 datemaydiffer = resolvecommitoptions(ui, opts)
2877
2879
2878 date = old.date()
2880 date = old.date()
2879 if opts.get(b'date'):
2881 if opts.get(b'date'):
2880 date = dateutil.parsedate(opts.get(b'date'))
2882 date = dateutil.parsedate(opts.get(b'date'))
2881 user = opts.get(b'user') or old.user()
2883 user = opts.get(b'user') or old.user()
2882
2884
2883 if len(old.parents()) > 1:
2885 if len(old.parents()) > 1:
2884 # ctx.files() isn't reliable for merges, so fall back to the
2886 # ctx.files() isn't reliable for merges, so fall back to the
2885 # slower repo.status() method
2887 # slower repo.status() method
2886 st = base.status(old)
2888 st = base.status(old)
2887 files = set(st.modified) | set(st.added) | set(st.removed)
2889 files = set(st.modified) | set(st.added) | set(st.removed)
2888 else:
2890 else:
2889 files = set(old.files())
2891 files = set(old.files())
2890
2892
2891 # add/remove the files to the working copy if the "addremove" option
2893 # add/remove the files to the working copy if the "addremove" option
2892 # was specified.
2894 # was specified.
2893 matcher = scmutil.match(wctx, pats, opts)
2895 matcher = scmutil.match(wctx, pats, opts)
2894 relative = scmutil.anypats(pats, opts)
2896 relative = scmutil.anypats(pats, opts)
2895 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2897 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2896 if opts.get(b'addremove') and scmutil.addremove(
2898 if opts.get(b'addremove') and scmutil.addremove(
2897 repo, matcher, b"", uipathfn, opts
2899 repo, matcher, b"", uipathfn, opts
2898 ):
2900 ):
2899 raise error.Abort(
2901 raise error.Abort(
2900 _(b"failed to mark all new/missing files as added/removed")
2902 _(b"failed to mark all new/missing files as added/removed")
2901 )
2903 )
2902
2904
2903 # Check subrepos. This depends on in-place wctx._status update in
2905 # Check subrepos. This depends on in-place wctx._status update in
2904 # subrepo.precommit(). To minimize the risk of this hack, we do
2906 # subrepo.precommit(). To minimize the risk of this hack, we do
2905 # nothing if .hgsub does not exist.
2907 # nothing if .hgsub does not exist.
2906 if b'.hgsub' in wctx or b'.hgsub' in old:
2908 if b'.hgsub' in wctx or b'.hgsub' in old:
2907 subs, commitsubs, newsubstate = subrepoutil.precommit(
2909 subs, commitsubs, newsubstate = subrepoutil.precommit(
2908 ui, wctx, wctx._status, matcher
2910 ui, wctx, wctx._status, matcher
2909 )
2911 )
2910 # amend should abort if commitsubrepos is enabled
2912 # amend should abort if commitsubrepos is enabled
2911 assert not commitsubs
2913 assert not commitsubs
2912 if subs:
2914 if subs:
2913 subrepoutil.writestate(repo, newsubstate)
2915 subrepoutil.writestate(repo, newsubstate)
2914
2916
2915 ms = mergemod.mergestate.read(repo)
2917 ms = mergemod.mergestate.read(repo)
2916 mergeutil.checkunresolved(ms)
2918 mergeutil.checkunresolved(ms)
2917
2919
2918 filestoamend = set(f for f in wctx.files() if matcher(f))
2920 filestoamend = set(f for f in wctx.files() if matcher(f))
2919
2921
2920 changes = len(filestoamend) > 0
2922 changes = len(filestoamend) > 0
2921 if changes:
2923 if changes:
2922 # Recompute copies (avoid recording a -> b -> a)
2924 # Recompute copies (avoid recording a -> b -> a)
2923 copied = copies.pathcopies(base, wctx, matcher)
2925 copied = copies.pathcopies(base, wctx, matcher)
2924 if old.p2:
2926 if old.p2:
2925 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2927 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2926
2928
2927 # Prune files which were reverted by the updates: if old
2929 # Prune files which were reverted by the updates: if old
2928 # introduced file X and the file was renamed in the working
2930 # introduced file X and the file was renamed in the working
2929 # copy, then those two files are the same and
2931 # copy, then those two files are the same and
2930 # we can discard X from our list of files. Likewise if X
2932 # we can discard X from our list of files. Likewise if X
2931 # was removed, it's no longer relevant. If X is missing (aka
2933 # was removed, it's no longer relevant. If X is missing (aka
2932 # deleted), old X must be preserved.
2934 # deleted), old X must be preserved.
2933 files.update(filestoamend)
2935 files.update(filestoamend)
2934 files = [
2936 files = [
2935 f
2937 f
2936 for f in files
2938 for f in files
2937 if (f not in filestoamend or not samefile(f, wctx, base))
2939 if (f not in filestoamend or not samefile(f, wctx, base))
2938 ]
2940 ]
2939
2941
2940 def filectxfn(repo, ctx_, path):
2942 def filectxfn(repo, ctx_, path):
2941 try:
2943 try:
2942 # If the file being considered is not amongst the files
2944 # If the file being considered is not amongst the files
2943 # to be amended, we should return the file context from the
2945 # to be amended, we should return the file context from the
2944 # old changeset. This avoids issues when only some files in
2946 # old changeset. This avoids issues when only some files in
2945 # the working copy are being amended but there are also
2947 # the working copy are being amended but there are also
2946 # changes to other files from the old changeset.
2948 # changes to other files from the old changeset.
2947 if path not in filestoamend:
2949 if path not in filestoamend:
2948 return old.filectx(path)
2950 return old.filectx(path)
2949
2951
2950 # Return None for removed files.
2952 # Return None for removed files.
2951 if path in wctx.removed():
2953 if path in wctx.removed():
2952 return None
2954 return None
2953
2955
2954 fctx = wctx[path]
2956 fctx = wctx[path]
2955 flags = fctx.flags()
2957 flags = fctx.flags()
2956 mctx = context.memfilectx(
2958 mctx = context.memfilectx(
2957 repo,
2959 repo,
2958 ctx_,
2960 ctx_,
2959 fctx.path(),
2961 fctx.path(),
2960 fctx.data(),
2962 fctx.data(),
2961 islink=b'l' in flags,
2963 islink=b'l' in flags,
2962 isexec=b'x' in flags,
2964 isexec=b'x' in flags,
2963 copysource=copied.get(path),
2965 copysource=copied.get(path),
2964 )
2966 )
2965 return mctx
2967 return mctx
2966 except KeyError:
2968 except KeyError:
2967 return None
2969 return None
2968
2970
2969 else:
2971 else:
2970 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2972 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2971
2973
2972 # Use version of files as in the old cset
2974 # Use version of files as in the old cset
2973 def filectxfn(repo, ctx_, path):
2975 def filectxfn(repo, ctx_, path):
2974 try:
2976 try:
2975 return old.filectx(path)
2977 return old.filectx(path)
2976 except KeyError:
2978 except KeyError:
2977 return None
2979 return None
2978
2980
2979 # See if we got a message from -m or -l, if not, open the editor with
2981 # See if we got a message from -m or -l, if not, open the editor with
2980 # the message of the changeset to amend.
2982 # the message of the changeset to amend.
2981 message = logmessage(ui, opts)
2983 message = logmessage(ui, opts)
2982
2984
2983 editform = mergeeditform(old, b'commit.amend')
2985 editform = mergeeditform(old, b'commit.amend')
2984
2986
2985 if not message:
2987 if not message:
2986 message = old.description()
2988 message = old.description()
2987 # Default if message isn't provided and --edit is not passed is to
2989 # Default if message isn't provided and --edit is not passed is to
2988 # invoke editor, but allow --no-edit. If somehow we don't have any
2990 # invoke editor, but allow --no-edit. If somehow we don't have any
2989 # description, let's always start the editor.
2991 # description, let's always start the editor.
2990 doedit = not message or opts.get(b'edit') in [True, None]
2992 doedit = not message or opts.get(b'edit') in [True, None]
2991 else:
2993 else:
2992 # Default if message is provided is to not invoke editor, but allow
2994 # Default if message is provided is to not invoke editor, but allow
2993 # --edit.
2995 # --edit.
2994 doedit = opts.get(b'edit') is True
2996 doedit = opts.get(b'edit') is True
2995 editor = getcommiteditor(edit=doedit, editform=editform)
2997 editor = getcommiteditor(edit=doedit, editform=editform)
2996
2998
2997 pureextra = extra.copy()
2999 pureextra = extra.copy()
2998 extra[b'amend_source'] = old.hex()
3000 extra[b'amend_source'] = old.hex()
2999
3001
3000 new = context.memctx(
3002 new = context.memctx(
3001 repo,
3003 repo,
3002 parents=[base.node(), old.p2().node()],
3004 parents=[base.node(), old.p2().node()],
3003 text=message,
3005 text=message,
3004 files=files,
3006 files=files,
3005 filectxfn=filectxfn,
3007 filectxfn=filectxfn,
3006 user=user,
3008 user=user,
3007 date=date,
3009 date=date,
3008 extra=extra,
3010 extra=extra,
3009 editor=editor,
3011 editor=editor,
3010 )
3012 )
3011
3013
3012 newdesc = changelog.stripdesc(new.description())
3014 newdesc = changelog.stripdesc(new.description())
3013 if (
3015 if (
3014 (not changes)
3016 (not changes)
3015 and newdesc == old.description()
3017 and newdesc == old.description()
3016 and user == old.user()
3018 and user == old.user()
3017 and (date == old.date() or datemaydiffer)
3019 and (date == old.date() or datemaydiffer)
3018 and pureextra == old.extra()
3020 and pureextra == old.extra()
3019 ):
3021 ):
3020 # nothing changed. continuing here would create a new node
3022 # nothing changed. continuing here would create a new node
3021 # anyway because of the amend_source noise.
3023 # anyway because of the amend_source noise.
3022 #
3024 #
3023 # This not what we expect from amend.
3025 # This not what we expect from amend.
3024 return old.node()
3026 return old.node()
3025
3027
3026 commitphase = None
3028 commitphase = None
3027 if opts.get(b'secret'):
3029 if opts.get(b'secret'):
3028 commitphase = phases.secret
3030 commitphase = phases.secret
3029 newid = repo.commitctx(new)
3031 newid = repo.commitctx(new)
3030
3032
3031 # Reroute the working copy parent to the new changeset
3033 # Reroute the working copy parent to the new changeset
3032 repo.setparents(newid, nullid)
3034 repo.setparents(newid, nullid)
3033 mapping = {old.node(): (newid,)}
3035 mapping = {old.node(): (newid,)}
3034 obsmetadata = None
3036 obsmetadata = None
3035 if opts.get(b'note'):
3037 if opts.get(b'note'):
3036 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3038 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3037 backup = ui.configbool(b'rewrite', b'backup-bundle')
3039 backup = ui.configbool(b'rewrite', b'backup-bundle')
3038 scmutil.cleanupnodes(
3040 scmutil.cleanupnodes(
3039 repo,
3041 repo,
3040 mapping,
3042 mapping,
3041 b'amend',
3043 b'amend',
3042 metadata=obsmetadata,
3044 metadata=obsmetadata,
3043 fixphase=True,
3045 fixphase=True,
3044 targetphase=commitphase,
3046 targetphase=commitphase,
3045 backup=backup,
3047 backup=backup,
3046 )
3048 )
3047
3049
3048 # Fixing the dirstate because localrepo.commitctx does not update
3050 # Fixing the dirstate because localrepo.commitctx does not update
3049 # it. This is rather convenient because we did not need to update
3051 # it. This is rather convenient because we did not need to update
3050 # the dirstate for all the files in the new commit which commitctx
3052 # the dirstate for all the files in the new commit which commitctx
3051 # could have done if it updated the dirstate. Now, we can
3053 # could have done if it updated the dirstate. Now, we can
3052 # selectively update the dirstate only for the amended files.
3054 # selectively update the dirstate only for the amended files.
3053 dirstate = repo.dirstate
3055 dirstate = repo.dirstate
3054
3056
3055 # Update the state of the files which were added and
3057 # Update the state of the files which were added and
3056 # and modified in the amend to "normal" in the dirstate.
3058 # and modified in the amend to "normal" in the dirstate.
3057 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3059 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3058 for f in normalfiles:
3060 for f in normalfiles:
3059 dirstate.normal(f)
3061 dirstate.normal(f)
3060
3062
3061 # Update the state of files which were removed in the amend
3063 # Update the state of files which were removed in the amend
3062 # to "removed" in the dirstate.
3064 # to "removed" in the dirstate.
3063 removedfiles = set(wctx.removed()) & filestoamend
3065 removedfiles = set(wctx.removed()) & filestoamend
3064 for f in removedfiles:
3066 for f in removedfiles:
3065 dirstate.drop(f)
3067 dirstate.drop(f)
3066
3068
3067 return newid
3069 return newid
3068
3070
3069
3071
3070 def commiteditor(repo, ctx, subs, editform=b''):
3072 def commiteditor(repo, ctx, subs, editform=b''):
3071 if ctx.description():
3073 if ctx.description():
3072 return ctx.description()
3074 return ctx.description()
3073 return commitforceeditor(
3075 return commitforceeditor(
3074 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3076 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3075 )
3077 )
3076
3078
3077
3079
3078 def commitforceeditor(
3080 def commitforceeditor(
3079 repo,
3081 repo,
3080 ctx,
3082 ctx,
3081 subs,
3083 subs,
3082 finishdesc=None,
3084 finishdesc=None,
3083 extramsg=None,
3085 extramsg=None,
3084 editform=b'',
3086 editform=b'',
3085 unchangedmessagedetection=False,
3087 unchangedmessagedetection=False,
3086 ):
3088 ):
3087 if not extramsg:
3089 if not extramsg:
3088 extramsg = _(b"Leave message empty to abort commit.")
3090 extramsg = _(b"Leave message empty to abort commit.")
3089
3091
3090 forms = [e for e in editform.split(b'.') if e]
3092 forms = [e for e in editform.split(b'.') if e]
3091 forms.insert(0, b'changeset')
3093 forms.insert(0, b'changeset')
3092 templatetext = None
3094 templatetext = None
3093 while forms:
3095 while forms:
3094 ref = b'.'.join(forms)
3096 ref = b'.'.join(forms)
3095 if repo.ui.config(b'committemplate', ref):
3097 if repo.ui.config(b'committemplate', ref):
3096 templatetext = committext = buildcommittemplate(
3098 templatetext = committext = buildcommittemplate(
3097 repo, ctx, subs, extramsg, ref
3099 repo, ctx, subs, extramsg, ref
3098 )
3100 )
3099 break
3101 break
3100 forms.pop()
3102 forms.pop()
3101 else:
3103 else:
3102 committext = buildcommittext(repo, ctx, subs, extramsg)
3104 committext = buildcommittext(repo, ctx, subs, extramsg)
3103
3105
3104 # run editor in the repository root
3106 # run editor in the repository root
3105 olddir = encoding.getcwd()
3107 olddir = encoding.getcwd()
3106 os.chdir(repo.root)
3108 os.chdir(repo.root)
3107
3109
3108 # make in-memory changes visible to external process
3110 # make in-memory changes visible to external process
3109 tr = repo.currenttransaction()
3111 tr = repo.currenttransaction()
3110 repo.dirstate.write(tr)
3112 repo.dirstate.write(tr)
3111 pending = tr and tr.writepending() and repo.root
3113 pending = tr and tr.writepending() and repo.root
3112
3114
3113 editortext = repo.ui.edit(
3115 editortext = repo.ui.edit(
3114 committext,
3116 committext,
3115 ctx.user(),
3117 ctx.user(),
3116 ctx.extra(),
3118 ctx.extra(),
3117 editform=editform,
3119 editform=editform,
3118 pending=pending,
3120 pending=pending,
3119 repopath=repo.path,
3121 repopath=repo.path,
3120 action=b'commit',
3122 action=b'commit',
3121 )
3123 )
3122 text = editortext
3124 text = editortext
3123
3125
3124 # strip away anything below this special string (used for editors that want
3126 # strip away anything below this special string (used for editors that want
3125 # to display the diff)
3127 # to display the diff)
3126 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3128 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3127 if stripbelow:
3129 if stripbelow:
3128 text = text[: stripbelow.start()]
3130 text = text[: stripbelow.start()]
3129
3131
3130 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3132 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3131 os.chdir(olddir)
3133 os.chdir(olddir)
3132
3134
3133 if finishdesc:
3135 if finishdesc:
3134 text = finishdesc(text)
3136 text = finishdesc(text)
3135 if not text.strip():
3137 if not text.strip():
3136 raise error.Abort(_(b"empty commit message"))
3138 raise error.Abort(_(b"empty commit message"))
3137 if unchangedmessagedetection and editortext == templatetext:
3139 if unchangedmessagedetection and editortext == templatetext:
3138 raise error.Abort(_(b"commit message unchanged"))
3140 raise error.Abort(_(b"commit message unchanged"))
3139
3141
3140 return text
3142 return text
3141
3143
3142
3144
3143 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3145 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3144 ui = repo.ui
3146 ui = repo.ui
3145 spec = formatter.templatespec(ref, None, None)
3147 spec = formatter.templatespec(ref, None, None)
3146 t = logcmdutil.changesettemplater(ui, repo, spec)
3148 t = logcmdutil.changesettemplater(ui, repo, spec)
3147 t.t.cache.update(
3149 t.t.cache.update(
3148 (k, templater.unquotestring(v))
3150 (k, templater.unquotestring(v))
3149 for k, v in repo.ui.configitems(b'committemplate')
3151 for k, v in repo.ui.configitems(b'committemplate')
3150 )
3152 )
3151
3153
3152 if not extramsg:
3154 if not extramsg:
3153 extramsg = b'' # ensure that extramsg is string
3155 extramsg = b'' # ensure that extramsg is string
3154
3156
3155 ui.pushbuffer()
3157 ui.pushbuffer()
3156 t.show(ctx, extramsg=extramsg)
3158 t.show(ctx, extramsg=extramsg)
3157 return ui.popbuffer()
3159 return ui.popbuffer()
3158
3160
3159
3161
3160 def hgprefix(msg):
3162 def hgprefix(msg):
3161 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3163 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3162
3164
3163
3165
3164 def buildcommittext(repo, ctx, subs, extramsg):
3166 def buildcommittext(repo, ctx, subs, extramsg):
3165 edittext = []
3167 edittext = []
3166 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3168 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3167 if ctx.description():
3169 if ctx.description():
3168 edittext.append(ctx.description())
3170 edittext.append(ctx.description())
3169 edittext.append(b"")
3171 edittext.append(b"")
3170 edittext.append(b"") # Empty line between message and comments.
3172 edittext.append(b"") # Empty line between message and comments.
3171 edittext.append(
3173 edittext.append(
3172 hgprefix(
3174 hgprefix(
3173 _(
3175 _(
3174 b"Enter commit message."
3176 b"Enter commit message."
3175 b" Lines beginning with 'HG:' are removed."
3177 b" Lines beginning with 'HG:' are removed."
3176 )
3178 )
3177 )
3179 )
3178 )
3180 )
3179 edittext.append(hgprefix(extramsg))
3181 edittext.append(hgprefix(extramsg))
3180 edittext.append(b"HG: --")
3182 edittext.append(b"HG: --")
3181 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3183 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3182 if ctx.p2():
3184 if ctx.p2():
3183 edittext.append(hgprefix(_(b"branch merge")))
3185 edittext.append(hgprefix(_(b"branch merge")))
3184 if ctx.branch():
3186 if ctx.branch():
3185 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3187 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3186 if bookmarks.isactivewdirparent(repo):
3188 if bookmarks.isactivewdirparent(repo):
3187 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3189 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3188 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3190 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3189 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3191 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3190 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3192 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3191 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3193 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3192 if not added and not modified and not removed:
3194 if not added and not modified and not removed:
3193 edittext.append(hgprefix(_(b"no files changed")))
3195 edittext.append(hgprefix(_(b"no files changed")))
3194 edittext.append(b"")
3196 edittext.append(b"")
3195
3197
3196 return b"\n".join(edittext)
3198 return b"\n".join(edittext)
3197
3199
3198
3200
3199 def commitstatus(repo, node, branch, bheads=None, opts=None):
3201 def commitstatus(repo, node, branch, bheads=None, opts=None):
3200 if opts is None:
3202 if opts is None:
3201 opts = {}
3203 opts = {}
3202 ctx = repo[node]
3204 ctx = repo[node]
3203 parents = ctx.parents()
3205 parents = ctx.parents()
3204
3206
3205 if (
3207 if (
3206 not opts.get(b'amend')
3208 not opts.get(b'amend')
3207 and bheads
3209 and bheads
3208 and node not in bheads
3210 and node not in bheads
3209 and not [
3211 and not [
3210 x for x in parents if x.node() in bheads and x.branch() == branch
3212 x for x in parents if x.node() in bheads and x.branch() == branch
3211 ]
3213 ]
3212 ):
3214 ):
3213 repo.ui.status(_(b'created new head\n'))
3215 repo.ui.status(_(b'created new head\n'))
3214 # The message is not printed for initial roots. For the other
3216 # The message is not printed for initial roots. For the other
3215 # changesets, it is printed in the following situations:
3217 # changesets, it is printed in the following situations:
3216 #
3218 #
3217 # Par column: for the 2 parents with ...
3219 # Par column: for the 2 parents with ...
3218 # N: null or no parent
3220 # N: null or no parent
3219 # B: parent is on another named branch
3221 # B: parent is on another named branch
3220 # C: parent is a regular non head changeset
3222 # C: parent is a regular non head changeset
3221 # H: parent was a branch head of the current branch
3223 # H: parent was a branch head of the current branch
3222 # Msg column: whether we print "created new head" message
3224 # Msg column: whether we print "created new head" message
3223 # In the following, it is assumed that there already exists some
3225 # In the following, it is assumed that there already exists some
3224 # initial branch heads of the current branch, otherwise nothing is
3226 # initial branch heads of the current branch, otherwise nothing is
3225 # printed anyway.
3227 # printed anyway.
3226 #
3228 #
3227 # Par Msg Comment
3229 # Par Msg Comment
3228 # N N y additional topo root
3230 # N N y additional topo root
3229 #
3231 #
3230 # B N y additional branch root
3232 # B N y additional branch root
3231 # C N y additional topo head
3233 # C N y additional topo head
3232 # H N n usual case
3234 # H N n usual case
3233 #
3235 #
3234 # B B y weird additional branch root
3236 # B B y weird additional branch root
3235 # C B y branch merge
3237 # C B y branch merge
3236 # H B n merge with named branch
3238 # H B n merge with named branch
3237 #
3239 #
3238 # C C y additional head from merge
3240 # C C y additional head from merge
3239 # C H n merge with a head
3241 # C H n merge with a head
3240 #
3242 #
3241 # H H n head merge: head count decreases
3243 # H H n head merge: head count decreases
3242
3244
3243 if not opts.get(b'close_branch'):
3245 if not opts.get(b'close_branch'):
3244 for r in parents:
3246 for r in parents:
3245 if r.closesbranch() and r.branch() == branch:
3247 if r.closesbranch() and r.branch() == branch:
3246 repo.ui.status(
3248 repo.ui.status(
3247 _(b'reopening closed branch head %d\n') % r.rev()
3249 _(b'reopening closed branch head %d\n') % r.rev()
3248 )
3250 )
3249
3251
3250 if repo.ui.debugflag:
3252 if repo.ui.debugflag:
3251 repo.ui.write(
3253 repo.ui.write(
3252 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3254 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3253 )
3255 )
3254 elif repo.ui.verbose:
3256 elif repo.ui.verbose:
3255 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3257 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3256
3258
3257
3259
3258 def postcommitstatus(repo, pats, opts):
3260 def postcommitstatus(repo, pats, opts):
3259 return repo.status(match=scmutil.match(repo[None], pats, opts))
3261 return repo.status(match=scmutil.match(repo[None], pats, opts))
3260
3262
3261
3263
3262 def revert(ui, repo, ctx, parents, *pats, **opts):
3264 def revert(ui, repo, ctx, parents, *pats, **opts):
3263 opts = pycompat.byteskwargs(opts)
3265 opts = pycompat.byteskwargs(opts)
3264 parent, p2 = parents
3266 parent, p2 = parents
3265 node = ctx.node()
3267 node = ctx.node()
3266
3268
3267 mf = ctx.manifest()
3269 mf = ctx.manifest()
3268 if node == p2:
3270 if node == p2:
3269 parent = p2
3271 parent = p2
3270
3272
3271 # need all matching names in dirstate and manifest of target rev,
3273 # need all matching names in dirstate and manifest of target rev,
3272 # so have to walk both. do not print errors if files exist in one
3274 # so have to walk both. do not print errors if files exist in one
3273 # but not other. in both cases, filesets should be evaluated against
3275 # but not other. in both cases, filesets should be evaluated against
3274 # workingctx to get consistent result (issue4497). this means 'set:**'
3276 # workingctx to get consistent result (issue4497). this means 'set:**'
3275 # cannot be used to select missing files from target rev.
3277 # cannot be used to select missing files from target rev.
3276
3278
3277 # `names` is a mapping for all elements in working copy and target revision
3279 # `names` is a mapping for all elements in working copy and target revision
3278 # The mapping is in the form:
3280 # The mapping is in the form:
3279 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3281 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3280 names = {}
3282 names = {}
3281 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3283 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3282
3284
3283 with repo.wlock():
3285 with repo.wlock():
3284 ## filling of the `names` mapping
3286 ## filling of the `names` mapping
3285 # walk dirstate to fill `names`
3287 # walk dirstate to fill `names`
3286
3288
3287 interactive = opts.get(b'interactive', False)
3289 interactive = opts.get(b'interactive', False)
3288 wctx = repo[None]
3290 wctx = repo[None]
3289 m = scmutil.match(wctx, pats, opts)
3291 m = scmutil.match(wctx, pats, opts)
3290
3292
3291 # we'll need this later
3293 # we'll need this later
3292 targetsubs = sorted(s for s in wctx.substate if m(s))
3294 targetsubs = sorted(s for s in wctx.substate if m(s))
3293
3295
3294 if not m.always():
3296 if not m.always():
3295 matcher = matchmod.badmatch(m, lambda x, y: False)
3297 matcher = matchmod.badmatch(m, lambda x, y: False)
3296 for abs in wctx.walk(matcher):
3298 for abs in wctx.walk(matcher):
3297 names[abs] = m.exact(abs)
3299 names[abs] = m.exact(abs)
3298
3300
3299 # walk target manifest to fill `names`
3301 # walk target manifest to fill `names`
3300
3302
3301 def badfn(path, msg):
3303 def badfn(path, msg):
3302 if path in names:
3304 if path in names:
3303 return
3305 return
3304 if path in ctx.substate:
3306 if path in ctx.substate:
3305 return
3307 return
3306 path_ = path + b'/'
3308 path_ = path + b'/'
3307 for f in names:
3309 for f in names:
3308 if f.startswith(path_):
3310 if f.startswith(path_):
3309 return
3311 return
3310 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3312 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3311
3313
3312 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3314 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3313 if abs not in names:
3315 if abs not in names:
3314 names[abs] = m.exact(abs)
3316 names[abs] = m.exact(abs)
3315
3317
3316 # Find status of all file in `names`.
3318 # Find status of all file in `names`.
3317 m = scmutil.matchfiles(repo, names)
3319 m = scmutil.matchfiles(repo, names)
3318
3320
3319 changes = repo.status(
3321 changes = repo.status(
3320 node1=node, match=m, unknown=True, ignored=True, clean=True
3322 node1=node, match=m, unknown=True, ignored=True, clean=True
3321 )
3323 )
3322 else:
3324 else:
3323 changes = repo.status(node1=node, match=m)
3325 changes = repo.status(node1=node, match=m)
3324 for kind in changes:
3326 for kind in changes:
3325 for abs in kind:
3327 for abs in kind:
3326 names[abs] = m.exact(abs)
3328 names[abs] = m.exact(abs)
3327
3329
3328 m = scmutil.matchfiles(repo, names)
3330 m = scmutil.matchfiles(repo, names)
3329
3331
3330 modified = set(changes.modified)
3332 modified = set(changes.modified)
3331 added = set(changes.added)
3333 added = set(changes.added)
3332 removed = set(changes.removed)
3334 removed = set(changes.removed)
3333 _deleted = set(changes.deleted)
3335 _deleted = set(changes.deleted)
3334 unknown = set(changes.unknown)
3336 unknown = set(changes.unknown)
3335 unknown.update(changes.ignored)
3337 unknown.update(changes.ignored)
3336 clean = set(changes.clean)
3338 clean = set(changes.clean)
3337 modadded = set()
3339 modadded = set()
3338
3340
3339 # We need to account for the state of the file in the dirstate,
3341 # We need to account for the state of the file in the dirstate,
3340 # even when we revert against something else than parent. This will
3342 # even when we revert against something else than parent. This will
3341 # slightly alter the behavior of revert (doing back up or not, delete
3343 # slightly alter the behavior of revert (doing back up or not, delete
3342 # or just forget etc).
3344 # or just forget etc).
3343 if parent == node:
3345 if parent == node:
3344 dsmodified = modified
3346 dsmodified = modified
3345 dsadded = added
3347 dsadded = added
3346 dsremoved = removed
3348 dsremoved = removed
3347 # store all local modifications, useful later for rename detection
3349 # store all local modifications, useful later for rename detection
3348 localchanges = dsmodified | dsadded
3350 localchanges = dsmodified | dsadded
3349 modified, added, removed = set(), set(), set()
3351 modified, added, removed = set(), set(), set()
3350 else:
3352 else:
3351 changes = repo.status(node1=parent, match=m)
3353 changes = repo.status(node1=parent, match=m)
3352 dsmodified = set(changes.modified)
3354 dsmodified = set(changes.modified)
3353 dsadded = set(changes.added)
3355 dsadded = set(changes.added)
3354 dsremoved = set(changes.removed)
3356 dsremoved = set(changes.removed)
3355 # store all local modifications, useful later for rename detection
3357 # store all local modifications, useful later for rename detection
3356 localchanges = dsmodified | dsadded
3358 localchanges = dsmodified | dsadded
3357
3359
3358 # only take into account for removes between wc and target
3360 # only take into account for removes between wc and target
3359 clean |= dsremoved - removed
3361 clean |= dsremoved - removed
3360 dsremoved &= removed
3362 dsremoved &= removed
3361 # distinct between dirstate remove and other
3363 # distinct between dirstate remove and other
3362 removed -= dsremoved
3364 removed -= dsremoved
3363
3365
3364 modadded = added & dsmodified
3366 modadded = added & dsmodified
3365 added -= modadded
3367 added -= modadded
3366
3368
3367 # tell newly modified apart.
3369 # tell newly modified apart.
3368 dsmodified &= modified
3370 dsmodified &= modified
3369 dsmodified |= modified & dsadded # dirstate added may need backup
3371 dsmodified |= modified & dsadded # dirstate added may need backup
3370 modified -= dsmodified
3372 modified -= dsmodified
3371
3373
3372 # We need to wait for some post-processing to update this set
3374 # We need to wait for some post-processing to update this set
3373 # before making the distinction. The dirstate will be used for
3375 # before making the distinction. The dirstate will be used for
3374 # that purpose.
3376 # that purpose.
3375 dsadded = added
3377 dsadded = added
3376
3378
3377 # in case of merge, files that are actually added can be reported as
3379 # in case of merge, files that are actually added can be reported as
3378 # modified, we need to post process the result
3380 # modified, we need to post process the result
3379 if p2 != nullid:
3381 if p2 != nullid:
3380 mergeadd = set(dsmodified)
3382 mergeadd = set(dsmodified)
3381 for path in dsmodified:
3383 for path in dsmodified:
3382 if path in mf:
3384 if path in mf:
3383 mergeadd.remove(path)
3385 mergeadd.remove(path)
3384 dsadded |= mergeadd
3386 dsadded |= mergeadd
3385 dsmodified -= mergeadd
3387 dsmodified -= mergeadd
3386
3388
3387 # if f is a rename, update `names` to also revert the source
3389 # if f is a rename, update `names` to also revert the source
3388 for f in localchanges:
3390 for f in localchanges:
3389 src = repo.dirstate.copied(f)
3391 src = repo.dirstate.copied(f)
3390 # XXX should we check for rename down to target node?
3392 # XXX should we check for rename down to target node?
3391 if src and src not in names and repo.dirstate[src] == b'r':
3393 if src and src not in names and repo.dirstate[src] == b'r':
3392 dsremoved.add(src)
3394 dsremoved.add(src)
3393 names[src] = True
3395 names[src] = True
3394
3396
3395 # determine the exact nature of the deleted changesets
3397 # determine the exact nature of the deleted changesets
3396 deladded = set(_deleted)
3398 deladded = set(_deleted)
3397 for path in _deleted:
3399 for path in _deleted:
3398 if path in mf:
3400 if path in mf:
3399 deladded.remove(path)
3401 deladded.remove(path)
3400 deleted = _deleted - deladded
3402 deleted = _deleted - deladded
3401
3403
3402 # distinguish between file to forget and the other
3404 # distinguish between file to forget and the other
3403 added = set()
3405 added = set()
3404 for abs in dsadded:
3406 for abs in dsadded:
3405 if repo.dirstate[abs] != b'a':
3407 if repo.dirstate[abs] != b'a':
3406 added.add(abs)
3408 added.add(abs)
3407 dsadded -= added
3409 dsadded -= added
3408
3410
3409 for abs in deladded:
3411 for abs in deladded:
3410 if repo.dirstate[abs] == b'a':
3412 if repo.dirstate[abs] == b'a':
3411 dsadded.add(abs)
3413 dsadded.add(abs)
3412 deladded -= dsadded
3414 deladded -= dsadded
3413
3415
3414 # For files marked as removed, we check if an unknown file is present at
3416 # For files marked as removed, we check if an unknown file is present at
3415 # the same path. If a such file exists it may need to be backed up.
3417 # the same path. If a such file exists it may need to be backed up.
3416 # Making the distinction at this stage helps have simpler backup
3418 # Making the distinction at this stage helps have simpler backup
3417 # logic.
3419 # logic.
3418 removunk = set()
3420 removunk = set()
3419 for abs in removed:
3421 for abs in removed:
3420 target = repo.wjoin(abs)
3422 target = repo.wjoin(abs)
3421 if os.path.lexists(target):
3423 if os.path.lexists(target):
3422 removunk.add(abs)
3424 removunk.add(abs)
3423 removed -= removunk
3425 removed -= removunk
3424
3426
3425 dsremovunk = set()
3427 dsremovunk = set()
3426 for abs in dsremoved:
3428 for abs in dsremoved:
3427 target = repo.wjoin(abs)
3429 target = repo.wjoin(abs)
3428 if os.path.lexists(target):
3430 if os.path.lexists(target):
3429 dsremovunk.add(abs)
3431 dsremovunk.add(abs)
3430 dsremoved -= dsremovunk
3432 dsremoved -= dsremovunk
3431
3433
3432 # action to be actually performed by revert
3434 # action to be actually performed by revert
3433 # (<list of file>, message>) tuple
3435 # (<list of file>, message>) tuple
3434 actions = {
3436 actions = {
3435 b'revert': ([], _(b'reverting %s\n')),
3437 b'revert': ([], _(b'reverting %s\n')),
3436 b'add': ([], _(b'adding %s\n')),
3438 b'add': ([], _(b'adding %s\n')),
3437 b'remove': ([], _(b'removing %s\n')),
3439 b'remove': ([], _(b'removing %s\n')),
3438 b'drop': ([], _(b'removing %s\n')),
3440 b'drop': ([], _(b'removing %s\n')),
3439 b'forget': ([], _(b'forgetting %s\n')),
3441 b'forget': ([], _(b'forgetting %s\n')),
3440 b'undelete': ([], _(b'undeleting %s\n')),
3442 b'undelete': ([], _(b'undeleting %s\n')),
3441 b'noop': (None, _(b'no changes needed to %s\n')),
3443 b'noop': (None, _(b'no changes needed to %s\n')),
3442 b'unknown': (None, _(b'file not managed: %s\n')),
3444 b'unknown': (None, _(b'file not managed: %s\n')),
3443 }
3445 }
3444
3446
3445 # "constant" that convey the backup strategy.
3447 # "constant" that convey the backup strategy.
3446 # All set to `discard` if `no-backup` is set do avoid checking
3448 # All set to `discard` if `no-backup` is set do avoid checking
3447 # no_backup lower in the code.
3449 # no_backup lower in the code.
3448 # These values are ordered for comparison purposes
3450 # These values are ordered for comparison purposes
3449 backupinteractive = 3 # do backup if interactively modified
3451 backupinteractive = 3 # do backup if interactively modified
3450 backup = 2 # unconditionally do backup
3452 backup = 2 # unconditionally do backup
3451 check = 1 # check if the existing file differs from target
3453 check = 1 # check if the existing file differs from target
3452 discard = 0 # never do backup
3454 discard = 0 # never do backup
3453 if opts.get(b'no_backup'):
3455 if opts.get(b'no_backup'):
3454 backupinteractive = backup = check = discard
3456 backupinteractive = backup = check = discard
3455 if interactive:
3457 if interactive:
3456 dsmodifiedbackup = backupinteractive
3458 dsmodifiedbackup = backupinteractive
3457 else:
3459 else:
3458 dsmodifiedbackup = backup
3460 dsmodifiedbackup = backup
3459 tobackup = set()
3461 tobackup = set()
3460
3462
3461 backupanddel = actions[b'remove']
3463 backupanddel = actions[b'remove']
3462 if not opts.get(b'no_backup'):
3464 if not opts.get(b'no_backup'):
3463 backupanddel = actions[b'drop']
3465 backupanddel = actions[b'drop']
3464
3466
3465 disptable = (
3467 disptable = (
3466 # dispatch table:
3468 # dispatch table:
3467 # file state
3469 # file state
3468 # action
3470 # action
3469 # make backup
3471 # make backup
3470 ## Sets that results that will change file on disk
3472 ## Sets that results that will change file on disk
3471 # Modified compared to target, no local change
3473 # Modified compared to target, no local change
3472 (modified, actions[b'revert'], discard),
3474 (modified, actions[b'revert'], discard),
3473 # Modified compared to target, but local file is deleted
3475 # Modified compared to target, but local file is deleted
3474 (deleted, actions[b'revert'], discard),
3476 (deleted, actions[b'revert'], discard),
3475 # Modified compared to target, local change
3477 # Modified compared to target, local change
3476 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3478 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3477 # Added since target
3479 # Added since target
3478 (added, actions[b'remove'], discard),
3480 (added, actions[b'remove'], discard),
3479 # Added in working directory
3481 # Added in working directory
3480 (dsadded, actions[b'forget'], discard),
3482 (dsadded, actions[b'forget'], discard),
3481 # Added since target, have local modification
3483 # Added since target, have local modification
3482 (modadded, backupanddel, backup),
3484 (modadded, backupanddel, backup),
3483 # Added since target but file is missing in working directory
3485 # Added since target but file is missing in working directory
3484 (deladded, actions[b'drop'], discard),
3486 (deladded, actions[b'drop'], discard),
3485 # Removed since target, before working copy parent
3487 # Removed since target, before working copy parent
3486 (removed, actions[b'add'], discard),
3488 (removed, actions[b'add'], discard),
3487 # Same as `removed` but an unknown file exists at the same path
3489 # Same as `removed` but an unknown file exists at the same path
3488 (removunk, actions[b'add'], check),
3490 (removunk, actions[b'add'], check),
3489 # Removed since targe, marked as such in working copy parent
3491 # Removed since targe, marked as such in working copy parent
3490 (dsremoved, actions[b'undelete'], discard),
3492 (dsremoved, actions[b'undelete'], discard),
3491 # Same as `dsremoved` but an unknown file exists at the same path
3493 # Same as `dsremoved` but an unknown file exists at the same path
3492 (dsremovunk, actions[b'undelete'], check),
3494 (dsremovunk, actions[b'undelete'], check),
3493 ## the following sets does not result in any file changes
3495 ## the following sets does not result in any file changes
3494 # File with no modification
3496 # File with no modification
3495 (clean, actions[b'noop'], discard),
3497 (clean, actions[b'noop'], discard),
3496 # Existing file, not tracked anywhere
3498 # Existing file, not tracked anywhere
3497 (unknown, actions[b'unknown'], discard),
3499 (unknown, actions[b'unknown'], discard),
3498 )
3500 )
3499
3501
3500 for abs, exact in sorted(names.items()):
3502 for abs, exact in sorted(names.items()):
3501 # target file to be touch on disk (relative to cwd)
3503 # target file to be touch on disk (relative to cwd)
3502 target = repo.wjoin(abs)
3504 target = repo.wjoin(abs)
3503 # search the entry in the dispatch table.
3505 # search the entry in the dispatch table.
3504 # if the file is in any of these sets, it was touched in the working
3506 # if the file is in any of these sets, it was touched in the working
3505 # directory parent and we are sure it needs to be reverted.
3507 # directory parent and we are sure it needs to be reverted.
3506 for table, (xlist, msg), dobackup in disptable:
3508 for table, (xlist, msg), dobackup in disptable:
3507 if abs not in table:
3509 if abs not in table:
3508 continue
3510 continue
3509 if xlist is not None:
3511 if xlist is not None:
3510 xlist.append(abs)
3512 xlist.append(abs)
3511 if dobackup:
3513 if dobackup:
3512 # If in interactive mode, don't automatically create
3514 # If in interactive mode, don't automatically create
3513 # .orig files (issue4793)
3515 # .orig files (issue4793)
3514 if dobackup == backupinteractive:
3516 if dobackup == backupinteractive:
3515 tobackup.add(abs)
3517 tobackup.add(abs)
3516 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3518 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3517 absbakname = scmutil.backuppath(ui, repo, abs)
3519 absbakname = scmutil.backuppath(ui, repo, abs)
3518 bakname = os.path.relpath(
3520 bakname = os.path.relpath(
3519 absbakname, start=repo.root
3521 absbakname, start=repo.root
3520 )
3522 )
3521 ui.note(
3523 ui.note(
3522 _(b'saving current version of %s as %s\n')
3524 _(b'saving current version of %s as %s\n')
3523 % (uipathfn(abs), uipathfn(bakname))
3525 % (uipathfn(abs), uipathfn(bakname))
3524 )
3526 )
3525 if not opts.get(b'dry_run'):
3527 if not opts.get(b'dry_run'):
3526 if interactive:
3528 if interactive:
3527 util.copyfile(target, absbakname)
3529 util.copyfile(target, absbakname)
3528 else:
3530 else:
3529 util.rename(target, absbakname)
3531 util.rename(target, absbakname)
3530 if opts.get(b'dry_run'):
3532 if opts.get(b'dry_run'):
3531 if ui.verbose or not exact:
3533 if ui.verbose or not exact:
3532 ui.status(msg % uipathfn(abs))
3534 ui.status(msg % uipathfn(abs))
3533 elif exact:
3535 elif exact:
3534 ui.warn(msg % uipathfn(abs))
3536 ui.warn(msg % uipathfn(abs))
3535 break
3537 break
3536
3538
3537 if not opts.get(b'dry_run'):
3539 if not opts.get(b'dry_run'):
3538 needdata = (b'revert', b'add', b'undelete')
3540 needdata = (b'revert', b'add', b'undelete')
3539 oplist = [actions[name][0] for name in needdata]
3541 oplist = [actions[name][0] for name in needdata]
3540 prefetch = scmutil.prefetchfiles
3542 prefetch = scmutil.prefetchfiles
3541 matchfiles = scmutil.matchfiles
3543 matchfiles = scmutil.matchfiles
3542 prefetch(
3544 prefetch(
3543 repo,
3545 repo,
3544 [ctx.rev()],
3546 [ctx.rev()],
3545 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3547 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3546 )
3548 )
3547 match = scmutil.match(repo[None], pats)
3549 match = scmutil.match(repo[None], pats)
3548 _performrevert(
3550 _performrevert(
3549 repo,
3551 repo,
3550 parents,
3552 parents,
3551 ctx,
3553 ctx,
3552 names,
3554 names,
3553 uipathfn,
3555 uipathfn,
3554 actions,
3556 actions,
3555 match,
3557 match,
3556 interactive,
3558 interactive,
3557 tobackup,
3559 tobackup,
3558 )
3560 )
3559
3561
3560 if targetsubs:
3562 if targetsubs:
3561 # Revert the subrepos on the revert list
3563 # Revert the subrepos on the revert list
3562 for sub in targetsubs:
3564 for sub in targetsubs:
3563 try:
3565 try:
3564 wctx.sub(sub).revert(
3566 wctx.sub(sub).revert(
3565 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3567 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3566 )
3568 )
3567 except KeyError:
3569 except KeyError:
3568 raise error.Abort(
3570 raise error.Abort(
3569 b"subrepository '%s' does not exist in %s!"
3571 b"subrepository '%s' does not exist in %s!"
3570 % (sub, short(ctx.node()))
3572 % (sub, short(ctx.node()))
3571 )
3573 )
3572
3574
3573
3575
3574 def _performrevert(
3576 def _performrevert(
3575 repo,
3577 repo,
3576 parents,
3578 parents,
3577 ctx,
3579 ctx,
3578 names,
3580 names,
3579 uipathfn,
3581 uipathfn,
3580 actions,
3582 actions,
3581 match,
3583 match,
3582 interactive=False,
3584 interactive=False,
3583 tobackup=None,
3585 tobackup=None,
3584 ):
3586 ):
3585 """function that actually perform all the actions computed for revert
3587 """function that actually perform all the actions computed for revert
3586
3588
3587 This is an independent function to let extension to plug in and react to
3589 This is an independent function to let extension to plug in and react to
3588 the imminent revert.
3590 the imminent revert.
3589
3591
3590 Make sure you have the working directory locked when calling this function.
3592 Make sure you have the working directory locked when calling this function.
3591 """
3593 """
3592 parent, p2 = parents
3594 parent, p2 = parents
3593 node = ctx.node()
3595 node = ctx.node()
3594 excluded_files = []
3596 excluded_files = []
3595
3597
3596 def checkout(f):
3598 def checkout(f):
3597 fc = ctx[f]
3599 fc = ctx[f]
3598 repo.wwrite(f, fc.data(), fc.flags())
3600 repo.wwrite(f, fc.data(), fc.flags())
3599
3601
3600 def doremove(f):
3602 def doremove(f):
3601 try:
3603 try:
3602 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3604 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3603 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3605 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3604 except OSError:
3606 except OSError:
3605 pass
3607 pass
3606 repo.dirstate.remove(f)
3608 repo.dirstate.remove(f)
3607
3609
3608 def prntstatusmsg(action, f):
3610 def prntstatusmsg(action, f):
3609 exact = names[f]
3611 exact = names[f]
3610 if repo.ui.verbose or not exact:
3612 if repo.ui.verbose or not exact:
3611 repo.ui.status(actions[action][1] % uipathfn(f))
3613 repo.ui.status(actions[action][1] % uipathfn(f))
3612
3614
3613 audit_path = pathutil.pathauditor(repo.root, cached=True)
3615 audit_path = pathutil.pathauditor(repo.root, cached=True)
3614 for f in actions[b'forget'][0]:
3616 for f in actions[b'forget'][0]:
3615 if interactive:
3617 if interactive:
3616 choice = repo.ui.promptchoice(
3618 choice = repo.ui.promptchoice(
3617 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3619 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3618 )
3620 )
3619 if choice == 0:
3621 if choice == 0:
3620 prntstatusmsg(b'forget', f)
3622 prntstatusmsg(b'forget', f)
3621 repo.dirstate.drop(f)
3623 repo.dirstate.drop(f)
3622 else:
3624 else:
3623 excluded_files.append(f)
3625 excluded_files.append(f)
3624 else:
3626 else:
3625 prntstatusmsg(b'forget', f)
3627 prntstatusmsg(b'forget', f)
3626 repo.dirstate.drop(f)
3628 repo.dirstate.drop(f)
3627 for f in actions[b'remove'][0]:
3629 for f in actions[b'remove'][0]:
3628 audit_path(f)
3630 audit_path(f)
3629 if interactive:
3631 if interactive:
3630 choice = repo.ui.promptchoice(
3632 choice = repo.ui.promptchoice(
3631 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3633 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3632 )
3634 )
3633 if choice == 0:
3635 if choice == 0:
3634 prntstatusmsg(b'remove', f)
3636 prntstatusmsg(b'remove', f)
3635 doremove(f)
3637 doremove(f)
3636 else:
3638 else:
3637 excluded_files.append(f)
3639 excluded_files.append(f)
3638 else:
3640 else:
3639 prntstatusmsg(b'remove', f)
3641 prntstatusmsg(b'remove', f)
3640 doremove(f)
3642 doremove(f)
3641 for f in actions[b'drop'][0]:
3643 for f in actions[b'drop'][0]:
3642 audit_path(f)
3644 audit_path(f)
3643 prntstatusmsg(b'drop', f)
3645 prntstatusmsg(b'drop', f)
3644 repo.dirstate.remove(f)
3646 repo.dirstate.remove(f)
3645
3647
3646 normal = None
3648 normal = None
3647 if node == parent:
3649 if node == parent:
3648 # We're reverting to our parent. If possible, we'd like status
3650 # We're reverting to our parent. If possible, we'd like status
3649 # to report the file as clean. We have to use normallookup for
3651 # to report the file as clean. We have to use normallookup for
3650 # merges to avoid losing information about merged/dirty files.
3652 # merges to avoid losing information about merged/dirty files.
3651 if p2 != nullid:
3653 if p2 != nullid:
3652 normal = repo.dirstate.normallookup
3654 normal = repo.dirstate.normallookup
3653 else:
3655 else:
3654 normal = repo.dirstate.normal
3656 normal = repo.dirstate.normal
3655
3657
3656 newlyaddedandmodifiedfiles = set()
3658 newlyaddedandmodifiedfiles = set()
3657 if interactive:
3659 if interactive:
3658 # Prompt the user for changes to revert
3660 # Prompt the user for changes to revert
3659 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3661 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3660 m = scmutil.matchfiles(repo, torevert)
3662 m = scmutil.matchfiles(repo, torevert)
3661 diffopts = patch.difffeatureopts(
3663 diffopts = patch.difffeatureopts(
3662 repo.ui,
3664 repo.ui,
3663 whitespace=True,
3665 whitespace=True,
3664 section=b'commands',
3666 section=b'commands',
3665 configprefix=b'revert.interactive.',
3667 configprefix=b'revert.interactive.',
3666 )
3668 )
3667 diffopts.nodates = True
3669 diffopts.nodates = True
3668 diffopts.git = True
3670 diffopts.git = True
3669 operation = b'apply'
3671 operation = b'apply'
3670 if node == parent:
3672 if node == parent:
3671 if repo.ui.configbool(
3673 if repo.ui.configbool(
3672 b'experimental', b'revert.interactive.select-to-keep'
3674 b'experimental', b'revert.interactive.select-to-keep'
3673 ):
3675 ):
3674 operation = b'keep'
3676 operation = b'keep'
3675 else:
3677 else:
3676 operation = b'discard'
3678 operation = b'discard'
3677
3679
3678 if operation == b'apply':
3680 if operation == b'apply':
3679 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3681 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3680 else:
3682 else:
3681 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3683 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3682 originalchunks = patch.parsepatch(diff)
3684 originalchunks = patch.parsepatch(diff)
3683
3685
3684 try:
3686 try:
3685
3687
3686 chunks, opts = recordfilter(
3688 chunks, opts = recordfilter(
3687 repo.ui, originalchunks, match, operation=operation
3689 repo.ui, originalchunks, match, operation=operation
3688 )
3690 )
3689 if operation == b'discard':
3691 if operation == b'discard':
3690 chunks = patch.reversehunks(chunks)
3692 chunks = patch.reversehunks(chunks)
3691
3693
3692 except error.PatchError as err:
3694 except error.PatchError as err:
3693 raise error.Abort(_(b'error parsing patch: %s') % err)
3695 raise error.Abort(_(b'error parsing patch: %s') % err)
3694
3696
3695 # FIXME: when doing an interactive revert of a copy, there's no way of
3697 # FIXME: when doing an interactive revert of a copy, there's no way of
3696 # performing a partial revert of the added file, the only option is
3698 # performing a partial revert of the added file, the only option is
3697 # "remove added file <name> (Yn)?", so we don't need to worry about the
3699 # "remove added file <name> (Yn)?", so we don't need to worry about the
3698 # alsorestore value. Ideally we'd be able to partially revert
3700 # alsorestore value. Ideally we'd be able to partially revert
3699 # copied/renamed files.
3701 # copied/renamed files.
3700 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3702 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3701 chunks, originalchunks
3703 chunks, originalchunks
3702 )
3704 )
3703 if tobackup is None:
3705 if tobackup is None:
3704 tobackup = set()
3706 tobackup = set()
3705 # Apply changes
3707 # Apply changes
3706 fp = stringio()
3708 fp = stringio()
3707 # chunks are serialized per file, but files aren't sorted
3709 # chunks are serialized per file, but files aren't sorted
3708 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3710 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3709 prntstatusmsg(b'revert', f)
3711 prntstatusmsg(b'revert', f)
3710 files = set()
3712 files = set()
3711 for c in chunks:
3713 for c in chunks:
3712 if ishunk(c):
3714 if ishunk(c):
3713 abs = c.header.filename()
3715 abs = c.header.filename()
3714 # Create a backup file only if this hunk should be backed up
3716 # Create a backup file only if this hunk should be backed up
3715 if c.header.filename() in tobackup:
3717 if c.header.filename() in tobackup:
3716 target = repo.wjoin(abs)
3718 target = repo.wjoin(abs)
3717 bakname = scmutil.backuppath(repo.ui, repo, abs)
3719 bakname = scmutil.backuppath(repo.ui, repo, abs)
3718 util.copyfile(target, bakname)
3720 util.copyfile(target, bakname)
3719 tobackup.remove(abs)
3721 tobackup.remove(abs)
3720 if abs not in files:
3722 if abs not in files:
3721 files.add(abs)
3723 files.add(abs)
3722 if operation == b'keep':
3724 if operation == b'keep':
3723 checkout(abs)
3725 checkout(abs)
3724 c.write(fp)
3726 c.write(fp)
3725 dopatch = fp.tell()
3727 dopatch = fp.tell()
3726 fp.seek(0)
3728 fp.seek(0)
3727 if dopatch:
3729 if dopatch:
3728 try:
3730 try:
3729 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3731 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3730 except error.PatchError as err:
3732 except error.PatchError as err:
3731 raise error.Abort(pycompat.bytestr(err))
3733 raise error.Abort(pycompat.bytestr(err))
3732 del fp
3734 del fp
3733 else:
3735 else:
3734 for f in actions[b'revert'][0]:
3736 for f in actions[b'revert'][0]:
3735 prntstatusmsg(b'revert', f)
3737 prntstatusmsg(b'revert', f)
3736 checkout(f)
3738 checkout(f)
3737 if normal:
3739 if normal:
3738 normal(f)
3740 normal(f)
3739
3741
3740 for f in actions[b'add'][0]:
3742 for f in actions[b'add'][0]:
3741 # Don't checkout modified files, they are already created by the diff
3743 # Don't checkout modified files, they are already created by the diff
3742 if f not in newlyaddedandmodifiedfiles:
3744 if f not in newlyaddedandmodifiedfiles:
3743 prntstatusmsg(b'add', f)
3745 prntstatusmsg(b'add', f)
3744 checkout(f)
3746 checkout(f)
3745 repo.dirstate.add(f)
3747 repo.dirstate.add(f)
3746
3748
3747 normal = repo.dirstate.normallookup
3749 normal = repo.dirstate.normallookup
3748 if node == parent and p2 == nullid:
3750 if node == parent and p2 == nullid:
3749 normal = repo.dirstate.normal
3751 normal = repo.dirstate.normal
3750 for f in actions[b'undelete'][0]:
3752 for f in actions[b'undelete'][0]:
3751 if interactive:
3753 if interactive:
3752 choice = repo.ui.promptchoice(
3754 choice = repo.ui.promptchoice(
3753 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3755 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3754 )
3756 )
3755 if choice == 0:
3757 if choice == 0:
3756 prntstatusmsg(b'undelete', f)
3758 prntstatusmsg(b'undelete', f)
3757 checkout(f)
3759 checkout(f)
3758 normal(f)
3760 normal(f)
3759 else:
3761 else:
3760 excluded_files.append(f)
3762 excluded_files.append(f)
3761 else:
3763 else:
3762 prntstatusmsg(b'undelete', f)
3764 prntstatusmsg(b'undelete', f)
3763 checkout(f)
3765 checkout(f)
3764 normal(f)
3766 normal(f)
3765
3767
3766 copied = copies.pathcopies(repo[parent], ctx)
3768 copied = copies.pathcopies(repo[parent], ctx)
3767
3769
3768 for f in (
3770 for f in (
3769 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3771 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3770 ):
3772 ):
3771 if f in copied:
3773 if f in copied:
3772 repo.dirstate.copy(copied[f], f)
3774 repo.dirstate.copy(copied[f], f)
3773
3775
3774
3776
3775 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3777 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3776 # commands.outgoing. "missing" is "missing" of the result of
3778 # commands.outgoing. "missing" is "missing" of the result of
3777 # "findcommonoutgoing()"
3779 # "findcommonoutgoing()"
3778 outgoinghooks = util.hooks()
3780 outgoinghooks = util.hooks()
3779
3781
3780 # a list of (ui, repo) functions called by commands.summary
3782 # a list of (ui, repo) functions called by commands.summary
3781 summaryhooks = util.hooks()
3783 summaryhooks = util.hooks()
3782
3784
3783 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3785 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3784 #
3786 #
3785 # functions should return tuple of booleans below, if 'changes' is None:
3787 # functions should return tuple of booleans below, if 'changes' is None:
3786 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3788 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3787 #
3789 #
3788 # otherwise, 'changes' is a tuple of tuples below:
3790 # otherwise, 'changes' is a tuple of tuples below:
3789 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3791 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3790 # - (desturl, destbranch, destpeer, outgoing)
3792 # - (desturl, destbranch, destpeer, outgoing)
3791 summaryremotehooks = util.hooks()
3793 summaryremotehooks = util.hooks()
3792
3794
3793
3795
3794 def checkunfinished(repo, commit=False, skipmerge=False):
3796 def checkunfinished(repo, commit=False, skipmerge=False):
3795 '''Look for an unfinished multistep operation, like graft, and abort
3797 '''Look for an unfinished multistep operation, like graft, and abort
3796 if found. It's probably good to check this right before
3798 if found. It's probably good to check this right before
3797 bailifchanged().
3799 bailifchanged().
3798 '''
3800 '''
3799 # Check for non-clearable states first, so things like rebase will take
3801 # Check for non-clearable states first, so things like rebase will take
3800 # precedence over update.
3802 # precedence over update.
3801 for state in statemod._unfinishedstates:
3803 for state in statemod._unfinishedstates:
3802 if (
3804 if (
3803 state._clearable
3805 state._clearable
3804 or (commit and state._allowcommit)
3806 or (commit and state._allowcommit)
3805 or state._reportonly
3807 or state._reportonly
3806 ):
3808 ):
3807 continue
3809 continue
3808 if state.isunfinished(repo):
3810 if state.isunfinished(repo):
3809 raise error.Abort(state.msg(), hint=state.hint())
3811 raise error.Abort(state.msg(), hint=state.hint())
3810
3812
3811 for s in statemod._unfinishedstates:
3813 for s in statemod._unfinishedstates:
3812 if (
3814 if (
3813 not s._clearable
3815 not s._clearable
3814 or (commit and s._allowcommit)
3816 or (commit and s._allowcommit)
3815 or (s._opname == b'merge' and skipmerge)
3817 or (s._opname == b'merge' and skipmerge)
3816 or s._reportonly
3818 or s._reportonly
3817 ):
3819 ):
3818 continue
3820 continue
3819 if s.isunfinished(repo):
3821 if s.isunfinished(repo):
3820 raise error.Abort(s.msg(), hint=s.hint())
3822 raise error.Abort(s.msg(), hint=s.hint())
3821
3823
3822
3824
3823 def clearunfinished(repo):
3825 def clearunfinished(repo):
3824 '''Check for unfinished operations (as above), and clear the ones
3826 '''Check for unfinished operations (as above), and clear the ones
3825 that are clearable.
3827 that are clearable.
3826 '''
3828 '''
3827 for state in statemod._unfinishedstates:
3829 for state in statemod._unfinishedstates:
3828 if state._reportonly:
3830 if state._reportonly:
3829 continue
3831 continue
3830 if not state._clearable and state.isunfinished(repo):
3832 if not state._clearable and state.isunfinished(repo):
3831 raise error.Abort(state.msg(), hint=state.hint())
3833 raise error.Abort(state.msg(), hint=state.hint())
3832
3834
3833 for s in statemod._unfinishedstates:
3835 for s in statemod._unfinishedstates:
3834 if s._opname == b'merge' or state._reportonly:
3836 if s._opname == b'merge' or state._reportonly:
3835 continue
3837 continue
3836 if s._clearable and s.isunfinished(repo):
3838 if s._clearable and s.isunfinished(repo):
3837 util.unlink(repo.vfs.join(s._fname))
3839 util.unlink(repo.vfs.join(s._fname))
3838
3840
3839
3841
3840 def getunfinishedstate(repo):
3842 def getunfinishedstate(repo):
3841 ''' Checks for unfinished operations and returns statecheck object
3843 ''' Checks for unfinished operations and returns statecheck object
3842 for it'''
3844 for it'''
3843 for state in statemod._unfinishedstates:
3845 for state in statemod._unfinishedstates:
3844 if state.isunfinished(repo):
3846 if state.isunfinished(repo):
3845 return state
3847 return state
3846 return None
3848 return None
3847
3849
3848
3850
3849 def howtocontinue(repo):
3851 def howtocontinue(repo):
3850 '''Check for an unfinished operation and return the command to finish
3852 '''Check for an unfinished operation and return the command to finish
3851 it.
3853 it.
3852
3854
3853 statemod._unfinishedstates list is checked for an unfinished operation
3855 statemod._unfinishedstates list is checked for an unfinished operation
3854 and the corresponding message to finish it is generated if a method to
3856 and the corresponding message to finish it is generated if a method to
3855 continue is supported by the operation.
3857 continue is supported by the operation.
3856
3858
3857 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3859 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3858 a boolean.
3860 a boolean.
3859 '''
3861 '''
3860 contmsg = _(b"continue: %s")
3862 contmsg = _(b"continue: %s")
3861 for state in statemod._unfinishedstates:
3863 for state in statemod._unfinishedstates:
3862 if not state._continueflag:
3864 if not state._continueflag:
3863 continue
3865 continue
3864 if state.isunfinished(repo):
3866 if state.isunfinished(repo):
3865 return contmsg % state.continuemsg(), True
3867 return contmsg % state.continuemsg(), True
3866 if repo[None].dirty(missing=True, merge=False, branch=False):
3868 if repo[None].dirty(missing=True, merge=False, branch=False):
3867 return contmsg % _(b"hg commit"), False
3869 return contmsg % _(b"hg commit"), False
3868 return None, None
3870 return None, None
3869
3871
3870
3872
3871 def checkafterresolved(repo):
3873 def checkafterresolved(repo):
3872 '''Inform the user about the next action after completing hg resolve
3874 '''Inform the user about the next action after completing hg resolve
3873
3875
3874 If there's a an unfinished operation that supports continue flag,
3876 If there's a an unfinished operation that supports continue flag,
3875 howtocontinue will yield repo.ui.warn as the reporter.
3877 howtocontinue will yield repo.ui.warn as the reporter.
3876
3878
3877 Otherwise, it will yield repo.ui.note.
3879 Otherwise, it will yield repo.ui.note.
3878 '''
3880 '''
3879 msg, warning = howtocontinue(repo)
3881 msg, warning = howtocontinue(repo)
3880 if msg is not None:
3882 if msg is not None:
3881 if warning:
3883 if warning:
3882 repo.ui.warn(b"%s\n" % msg)
3884 repo.ui.warn(b"%s\n" % msg)
3883 else:
3885 else:
3884 repo.ui.note(b"%s\n" % msg)
3886 repo.ui.note(b"%s\n" % msg)
3885
3887
3886
3888
3887 def wrongtooltocontinue(repo, task):
3889 def wrongtooltocontinue(repo, task):
3888 '''Raise an abort suggesting how to properly continue if there is an
3890 '''Raise an abort suggesting how to properly continue if there is an
3889 active task.
3891 active task.
3890
3892
3891 Uses howtocontinue() to find the active task.
3893 Uses howtocontinue() to find the active task.
3892
3894
3893 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3895 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3894 a hint.
3896 a hint.
3895 '''
3897 '''
3896 after = howtocontinue(repo)
3898 after = howtocontinue(repo)
3897 hint = None
3899 hint = None
3898 if after[1]:
3900 if after[1]:
3899 hint = after[0]
3901 hint = after[0]
3900 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3902 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3901
3903
3902
3904
3903 def abortgraft(ui, repo, graftstate):
3905 def abortgraft(ui, repo, graftstate):
3904 """abort the interrupted graft and rollbacks to the state before interrupted
3906 """abort the interrupted graft and rollbacks to the state before interrupted
3905 graft"""
3907 graft"""
3906 if not graftstate.exists():
3908 if not graftstate.exists():
3907 raise error.Abort(_(b"no interrupted graft to abort"))
3909 raise error.Abort(_(b"no interrupted graft to abort"))
3908 statedata = readgraftstate(repo, graftstate)
3910 statedata = readgraftstate(repo, graftstate)
3909 newnodes = statedata.get(b'newnodes')
3911 newnodes = statedata.get(b'newnodes')
3910 if newnodes is None:
3912 if newnodes is None:
3911 # and old graft state which does not have all the data required to abort
3913 # and old graft state which does not have all the data required to abort
3912 # the graft
3914 # the graft
3913 raise error.Abort(_(b"cannot abort using an old graftstate"))
3915 raise error.Abort(_(b"cannot abort using an old graftstate"))
3914
3916
3915 # changeset from which graft operation was started
3917 # changeset from which graft operation was started
3916 if len(newnodes) > 0:
3918 if len(newnodes) > 0:
3917 startctx = repo[newnodes[0]].p1()
3919 startctx = repo[newnodes[0]].p1()
3918 else:
3920 else:
3919 startctx = repo[b'.']
3921 startctx = repo[b'.']
3920 # whether to strip or not
3922 # whether to strip or not
3921 cleanup = False
3923 cleanup = False
3922 from . import hg
3924 from . import hg
3923
3925
3924 if newnodes:
3926 if newnodes:
3925 newnodes = [repo[r].rev() for r in newnodes]
3927 newnodes = [repo[r].rev() for r in newnodes]
3926 cleanup = True
3928 cleanup = True
3927 # checking that none of the newnodes turned public or is public
3929 # checking that none of the newnodes turned public or is public
3928 immutable = [c for c in newnodes if not repo[c].mutable()]
3930 immutable = [c for c in newnodes if not repo[c].mutable()]
3929 if immutable:
3931 if immutable:
3930 repo.ui.warn(
3932 repo.ui.warn(
3931 _(b"cannot clean up public changesets %s\n")
3933 _(b"cannot clean up public changesets %s\n")
3932 % b', '.join(bytes(repo[r]) for r in immutable),
3934 % b', '.join(bytes(repo[r]) for r in immutable),
3933 hint=_(b"see 'hg help phases' for details"),
3935 hint=_(b"see 'hg help phases' for details"),
3934 )
3936 )
3935 cleanup = False
3937 cleanup = False
3936
3938
3937 # checking that no new nodes are created on top of grafted revs
3939 # checking that no new nodes are created on top of grafted revs
3938 desc = set(repo.changelog.descendants(newnodes))
3940 desc = set(repo.changelog.descendants(newnodes))
3939 if desc - set(newnodes):
3941 if desc - set(newnodes):
3940 repo.ui.warn(
3942 repo.ui.warn(
3941 _(
3943 _(
3942 b"new changesets detected on destination "
3944 b"new changesets detected on destination "
3943 b"branch, can't strip\n"
3945 b"branch, can't strip\n"
3944 )
3946 )
3945 )
3947 )
3946 cleanup = False
3948 cleanup = False
3947
3949
3948 if cleanup:
3950 if cleanup:
3949 with repo.wlock(), repo.lock():
3951 with repo.wlock(), repo.lock():
3950 hg.updaterepo(repo, startctx.node(), overwrite=True)
3952 hg.updaterepo(repo, startctx.node(), overwrite=True)
3951 # stripping the new nodes created
3953 # stripping the new nodes created
3952 strippoints = [
3954 strippoints = [
3953 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3955 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3954 ]
3956 ]
3955 repair.strip(repo.ui, repo, strippoints, backup=False)
3957 repair.strip(repo.ui, repo, strippoints, backup=False)
3956
3958
3957 if not cleanup:
3959 if not cleanup:
3958 # we don't update to the startnode if we can't strip
3960 # we don't update to the startnode if we can't strip
3959 startctx = repo[b'.']
3961 startctx = repo[b'.']
3960 hg.updaterepo(repo, startctx.node(), overwrite=True)
3962 hg.updaterepo(repo, startctx.node(), overwrite=True)
3961
3963
3962 ui.status(_(b"graft aborted\n"))
3964 ui.status(_(b"graft aborted\n"))
3963 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3965 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3964 graftstate.delete()
3966 graftstate.delete()
3965 return 0
3967 return 0
3966
3968
3967
3969
3968 def readgraftstate(repo, graftstate):
3970 def readgraftstate(repo, graftstate):
3969 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3971 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3970 """read the graft state file and return a dict of the data stored in it"""
3972 """read the graft state file and return a dict of the data stored in it"""
3971 try:
3973 try:
3972 return graftstate.read()
3974 return graftstate.read()
3973 except error.CorruptedState:
3975 except error.CorruptedState:
3974 nodes = repo.vfs.read(b'graftstate').splitlines()
3976 nodes = repo.vfs.read(b'graftstate').splitlines()
3975 return {b'nodes': nodes}
3977 return {b'nodes': nodes}
3976
3978
3977
3979
3978 def hgabortgraft(ui, repo):
3980 def hgabortgraft(ui, repo):
3979 """ abort logic for aborting graft using 'hg abort'"""
3981 """ abort logic for aborting graft using 'hg abort'"""
3980 with repo.wlock():
3982 with repo.wlock():
3981 graftstate = statemod.cmdstate(repo, b'graftstate')
3983 graftstate = statemod.cmdstate(repo, b'graftstate')
3982 return abortgraft(ui, repo, graftstate)
3984 return abortgraft(ui, repo, graftstate)
@@ -1,7829 +1,7832 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from .pycompat import open
25 from .pycompat import open
26 from . import (
26 from . import (
27 archival,
27 archival,
28 bookmarks,
28 bookmarks,
29 bundle2,
29 bundle2,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 merge as mergemod,
48 merge as mergemod,
49 narrowspec,
49 narrowspec,
50 obsolete,
50 obsolete,
51 obsutil,
51 obsutil,
52 patch,
52 patch,
53 phases,
53 phases,
54 pycompat,
54 pycompat,
55 rcutil,
55 rcutil,
56 registrar,
56 registrar,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 shelve as shelvemod,
61 shelve as shelvemod,
62 state as statemod,
62 state as statemod,
63 streamclone,
63 streamclone,
64 tags as tagsmod,
64 tags as tagsmod,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 verify as verifymod,
67 verify as verifymod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 )
73 )
74
74
75 table = {}
75 table = {}
76 table.update(debugcommandsmod.command._table)
76 table.update(debugcommandsmod.command._table)
77
77
78 command = registrar.command(table)
78 command = registrar.command(table)
79 INTENT_READONLY = registrar.INTENT_READONLY
79 INTENT_READONLY = registrar.INTENT_READONLY
80
80
81 # common command options
81 # common command options
82
82
83 globalopts = [
83 globalopts = [
84 (
84 (
85 b'R',
85 b'R',
86 b'repository',
86 b'repository',
87 b'',
87 b'',
88 _(b'repository root directory or name of overlay bundle file'),
88 _(b'repository root directory or name of overlay bundle file'),
89 _(b'REPO'),
89 _(b'REPO'),
90 ),
90 ),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (
92 (
93 b'y',
93 b'y',
94 b'noninteractive',
94 b'noninteractive',
95 None,
95 None,
96 _(
96 _(
97 b'do not prompt, automatically pick the first choice for all prompts'
97 b'do not prompt, automatically pick the first choice for all prompts'
98 ),
98 ),
99 ),
99 ),
100 (b'q', b'quiet', None, _(b'suppress output')),
100 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
102 (
102 (
103 b'',
103 b'',
104 b'color',
104 b'color',
105 b'',
105 b'',
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # and should not be translated
107 # and should not be translated
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b'TYPE'),
109 _(b'TYPE'),
110 ),
110 ),
111 (
111 (
112 b'',
112 b'',
113 b'config',
113 b'config',
114 [],
114 [],
115 _(b'set/override config option (use \'section.name=value\')'),
115 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'CONFIG'),
116 _(b'CONFIG'),
117 ),
117 ),
118 (b'', b'debug', None, _(b'enable debugging output')),
118 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debugger', None, _(b'start debugger')),
119 (b'', b'debugger', None, _(b'start debugger')),
120 (
120 (
121 b'',
121 b'',
122 b'encoding',
122 b'encoding',
123 encoding.encoding,
123 encoding.encoding,
124 _(b'set the charset encoding'),
124 _(b'set the charset encoding'),
125 _(b'ENCODE'),
125 _(b'ENCODE'),
126 ),
126 ),
127 (
127 (
128 b'',
128 b'',
129 b'encodingmode',
129 b'encodingmode',
130 encoding.encodingmode,
130 encoding.encodingmode,
131 _(b'set the charset encoding mode'),
131 _(b'set the charset encoding mode'),
132 _(b'MODE'),
132 _(b'MODE'),
133 ),
133 ),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'time', None, _(b'time how long the command takes')),
135 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'profile', None, _(b'print command execution profile')),
136 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'version', None, _(b'output version information and exit')),
137 (b'', b'version', None, _(b'output version information and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (
140 (
141 b'',
141 b'',
142 b'pager',
142 b'pager',
143 b'auto',
143 b'auto',
144 _(b"when to paginate (boolean, always, auto, or never)"),
144 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b'TYPE'),
145 _(b'TYPE'),
146 ),
146 ),
147 ]
147 ]
148
148
149 dryrunopts = cmdutil.dryrunopts
149 dryrunopts = cmdutil.dryrunopts
150 remoteopts = cmdutil.remoteopts
150 remoteopts = cmdutil.remoteopts
151 walkopts = cmdutil.walkopts
151 walkopts = cmdutil.walkopts
152 commitopts = cmdutil.commitopts
152 commitopts = cmdutil.commitopts
153 commitopts2 = cmdutil.commitopts2
153 commitopts2 = cmdutil.commitopts2
154 commitopts3 = cmdutil.commitopts3
154 commitopts3 = cmdutil.commitopts3
155 formatteropts = cmdutil.formatteropts
155 formatteropts = cmdutil.formatteropts
156 templateopts = cmdutil.templateopts
156 templateopts = cmdutil.templateopts
157 logopts = cmdutil.logopts
157 logopts = cmdutil.logopts
158 diffopts = cmdutil.diffopts
158 diffopts = cmdutil.diffopts
159 diffwsopts = cmdutil.diffwsopts
159 diffwsopts = cmdutil.diffwsopts
160 diffopts2 = cmdutil.diffopts2
160 diffopts2 = cmdutil.diffopts2
161 mergetoolopts = cmdutil.mergetoolopts
161 mergetoolopts = cmdutil.mergetoolopts
162 similarityopts = cmdutil.similarityopts
162 similarityopts = cmdutil.similarityopts
163 subrepoopts = cmdutil.subrepoopts
163 subrepoopts = cmdutil.subrepoopts
164 debugrevlogopts = cmdutil.debugrevlogopts
164 debugrevlogopts = cmdutil.debugrevlogopts
165
165
166 # Commands start here, listed alphabetically
166 # Commands start here, listed alphabetically
167
167
168
168
169 @command(
169 @command(
170 b'abort',
170 b'abort',
171 dryrunopts,
171 dryrunopts,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpbasic=True,
173 helpbasic=True,
174 )
174 )
175 def abort(ui, repo, **opts):
175 def abort(ui, repo, **opts):
176 """abort an unfinished operation (EXPERIMENTAL)
176 """abort an unfinished operation (EXPERIMENTAL)
177
177
178 Aborts a multistep operation like graft, histedit, rebase, merge,
178 Aborts a multistep operation like graft, histedit, rebase, merge,
179 and unshelve if they are in an unfinished state.
179 and unshelve if they are in an unfinished state.
180
180
181 use --dry-run/-n to dry run the command.
181 use --dry-run/-n to dry run the command.
182 """
182 """
183 dryrun = opts.get('dry_run')
183 dryrun = opts.get('dry_run')
184 abortstate = cmdutil.getunfinishedstate(repo)
184 abortstate = cmdutil.getunfinishedstate(repo)
185 if not abortstate:
185 if not abortstate:
186 raise error.Abort(_(b'no operation in progress'))
186 raise error.Abort(_(b'no operation in progress'))
187 if not abortstate.abortfunc:
187 if not abortstate.abortfunc:
188 raise error.Abort(
188 raise error.Abort(
189 (
189 (
190 _(b"%s in progress but does not support 'hg abort'")
190 _(b"%s in progress but does not support 'hg abort'")
191 % (abortstate._opname)
191 % (abortstate._opname)
192 ),
192 ),
193 hint=abortstate.hint(),
193 hint=abortstate.hint(),
194 )
194 )
195 if dryrun:
195 if dryrun:
196 ui.status(
196 ui.status(
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 )
198 )
199 return
199 return
200 return abortstate.abortfunc(ui, repo)
200 return abortstate.abortfunc(ui, repo)
201
201
202
202
203 @command(
203 @command(
204 b'add',
204 b'add',
205 walkopts + subrepoopts + dryrunopts,
205 walkopts + subrepoopts + dryrunopts,
206 _(b'[OPTION]... [FILE]...'),
206 _(b'[OPTION]... [FILE]...'),
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpbasic=True,
208 helpbasic=True,
209 inferrepo=True,
209 inferrepo=True,
210 )
210 )
211 def add(ui, repo, *pats, **opts):
211 def add(ui, repo, *pats, **opts):
212 """add the specified files on the next commit
212 """add the specified files on the next commit
213
213
214 Schedule files to be version controlled and added to the
214 Schedule files to be version controlled and added to the
215 repository.
215 repository.
216
216
217 The files will be added to the repository at the next commit. To
217 The files will be added to the repository at the next commit. To
218 undo an add before that, see :hg:`forget`.
218 undo an add before that, see :hg:`forget`.
219
219
220 If no names are given, add all files to the repository (except
220 If no names are given, add all files to the repository (except
221 files matching ``.hgignore``).
221 files matching ``.hgignore``).
222
222
223 .. container:: verbose
223 .. container:: verbose
224
224
225 Examples:
225 Examples:
226
226
227 - New (unknown) files are added
227 - New (unknown) files are added
228 automatically by :hg:`add`::
228 automatically by :hg:`add`::
229
229
230 $ ls
230 $ ls
231 foo.c
231 foo.c
232 $ hg status
232 $ hg status
233 ? foo.c
233 ? foo.c
234 $ hg add
234 $ hg add
235 adding foo.c
235 adding foo.c
236 $ hg status
236 $ hg status
237 A foo.c
237 A foo.c
238
238
239 - Specific files to be added can be specified::
239 - Specific files to be added can be specified::
240
240
241 $ ls
241 $ ls
242 bar.c foo.c
242 bar.c foo.c
243 $ hg status
243 $ hg status
244 ? bar.c
244 ? bar.c
245 ? foo.c
245 ? foo.c
246 $ hg add bar.c
246 $ hg add bar.c
247 $ hg status
247 $ hg status
248 A bar.c
248 A bar.c
249 ? foo.c
249 ? foo.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253
253
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 return rejected and 1 or 0
257 return rejected and 1 or 0
258
258
259
259
260 @command(
260 @command(
261 b'addremove',
261 b'addremove',
262 similarityopts + subrepoopts + walkopts + dryrunopts,
262 similarityopts + subrepoopts + walkopts + dryrunopts,
263 _(b'[OPTION]... [FILE]...'),
263 _(b'[OPTION]... [FILE]...'),
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 inferrepo=True,
265 inferrepo=True,
266 )
266 )
267 def addremove(ui, repo, *pats, **opts):
267 def addremove(ui, repo, *pats, **opts):
268 """add all new files, delete all missing files
268 """add all new files, delete all missing files
269
269
270 Add all new files and remove all missing files from the
270 Add all new files and remove all missing files from the
271 repository.
271 repository.
272
272
273 Unless names are given, new files are ignored if they match any of
273 Unless names are given, new files are ignored if they match any of
274 the patterns in ``.hgignore``. As with add, these changes take
274 the patterns in ``.hgignore``. As with add, these changes take
275 effect at the next commit.
275 effect at the next commit.
276
276
277 Use the -s/--similarity option to detect renamed files. This
277 Use the -s/--similarity option to detect renamed files. This
278 option takes a percentage between 0 (disabled) and 100 (files must
278 option takes a percentage between 0 (disabled) and 100 (files must
279 be identical) as its parameter. With a parameter greater than 0,
279 be identical) as its parameter. With a parameter greater than 0,
280 this compares every removed file with every added file and records
280 this compares every removed file with every added file and records
281 those similar enough as renames. Detecting renamed files this way
281 those similar enough as renames. Detecting renamed files this way
282 can be expensive. After using this option, :hg:`status -C` can be
282 can be expensive. After using this option, :hg:`status -C` can be
283 used to check which files were identified as moved or renamed. If
283 used to check which files were identified as moved or renamed. If
284 not specified, -s/--similarity defaults to 100 and only renames of
284 not specified, -s/--similarity defaults to 100 and only renames of
285 identical files are detected.
285 identical files are detected.
286
286
287 .. container:: verbose
287 .. container:: verbose
288
288
289 Examples:
289 Examples:
290
290
291 - A number of files (bar.c and foo.c) are new,
291 - A number of files (bar.c and foo.c) are new,
292 while foobar.c has been removed (without using :hg:`remove`)
292 while foobar.c has been removed (without using :hg:`remove`)
293 from the repository::
293 from the repository::
294
294
295 $ ls
295 $ ls
296 bar.c foo.c
296 bar.c foo.c
297 $ hg status
297 $ hg status
298 ! foobar.c
298 ! foobar.c
299 ? bar.c
299 ? bar.c
300 ? foo.c
300 ? foo.c
301 $ hg addremove
301 $ hg addremove
302 adding bar.c
302 adding bar.c
303 adding foo.c
303 adding foo.c
304 removing foobar.c
304 removing foobar.c
305 $ hg status
305 $ hg status
306 A bar.c
306 A bar.c
307 A foo.c
307 A foo.c
308 R foobar.c
308 R foobar.c
309
309
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 Afterwards, it was edited slightly::
311 Afterwards, it was edited slightly::
312
312
313 $ ls
313 $ ls
314 foo.c
314 foo.c
315 $ hg status
315 $ hg status
316 ! foobar.c
316 ! foobar.c
317 ? foo.c
317 ? foo.c
318 $ hg addremove --similarity 90
318 $ hg addremove --similarity 90
319 removing foobar.c
319 removing foobar.c
320 adding foo.c
320 adding foo.c
321 recording removal of foobar.c as rename to foo.c (94% similar)
321 recording removal of foobar.c as rename to foo.c (94% similar)
322 $ hg status -C
322 $ hg status -C
323 A foo.c
323 A foo.c
324 foobar.c
324 foobar.c
325 R foobar.c
325 R foobar.c
326
326
327 Returns 0 if all files are successfully added.
327 Returns 0 if all files are successfully added.
328 """
328 """
329 opts = pycompat.byteskwargs(opts)
329 opts = pycompat.byteskwargs(opts)
330 if not opts.get(b'similarity'):
330 if not opts.get(b'similarity'):
331 opts[b'similarity'] = b'100'
331 opts[b'similarity'] = b'100'
332 matcher = scmutil.match(repo[None], pats, opts)
332 matcher = scmutil.match(repo[None], pats, opts)
333 relative = scmutil.anypats(pats, opts)
333 relative = scmutil.anypats(pats, opts)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336
336
337
337
338 @command(
338 @command(
339 b'annotate|blame',
339 b'annotate|blame',
340 [
340 [
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (
342 (
343 b'',
343 b'',
344 b'follow',
344 b'follow',
345 None,
345 None,
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 ),
347 ),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'a', b'text', None, _(b'treat all files as text')),
349 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'f', b'file', None, _(b'list the filename')),
351 (b'f', b'file', None, _(b'list the filename')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
355 (
355 (
356 b'l',
356 b'l',
357 b'line-number',
357 b'line-number',
358 None,
358 None,
359 _(b'show line number at the first appearance'),
359 _(b'show line number at the first appearance'),
360 ),
360 ),
361 (
361 (
362 b'',
362 b'',
363 b'skip',
363 b'skip',
364 [],
364 [],
365 _(b'revision to not display (EXPERIMENTAL)'),
365 _(b'revision to not display (EXPERIMENTAL)'),
366 _(b'REV'),
366 _(b'REV'),
367 ),
367 ),
368 ]
368 ]
369 + diffwsopts
369 + diffwsopts
370 + walkopts
370 + walkopts
371 + formatteropts,
371 + formatteropts,
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpbasic=True,
374 helpbasic=True,
375 inferrepo=True,
375 inferrepo=True,
376 )
376 )
377 def annotate(ui, repo, *pats, **opts):
377 def annotate(ui, repo, *pats, **opts):
378 """show changeset information by line for each file
378 """show changeset information by line for each file
379
379
380 List changes in files, showing the revision id responsible for
380 List changes in files, showing the revision id responsible for
381 each line.
381 each line.
382
382
383 This command is useful for discovering when a change was made and
383 This command is useful for discovering when a change was made and
384 by whom.
384 by whom.
385
385
386 If you include --file, --user, or --date, the revision number is
386 If you include --file, --user, or --date, the revision number is
387 suppressed unless you also include --number.
387 suppressed unless you also include --number.
388
388
389 Without the -a/--text option, annotate will avoid processing files
389 Without the -a/--text option, annotate will avoid processing files
390 it detects as binary. With -a, annotate will annotate the file
390 it detects as binary. With -a, annotate will annotate the file
391 anyway, although the results will probably be neither useful
391 anyway, although the results will probably be neither useful
392 nor desirable.
392 nor desirable.
393
393
394 .. container:: verbose
394 .. container:: verbose
395
395
396 Template:
396 Template:
397
397
398 The following keywords are supported in addition to the common template
398 The following keywords are supported in addition to the common template
399 keywords and functions. See also :hg:`help templates`.
399 keywords and functions. See also :hg:`help templates`.
400
400
401 :lines: List of lines with annotation data.
401 :lines: List of lines with annotation data.
402 :path: String. Repository-absolute path of the specified file.
402 :path: String. Repository-absolute path of the specified file.
403
403
404 And each entry of ``{lines}`` provides the following sub-keywords in
404 And each entry of ``{lines}`` provides the following sub-keywords in
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406
406
407 :line: String. Line content.
407 :line: String. Line content.
408 :lineno: Integer. Line number at that revision.
408 :lineno: Integer. Line number at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
410
410
411 See :hg:`help templates.operators` for the list expansion syntax.
411 See :hg:`help templates.operators` for the list expansion syntax.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 """
414 """
415 opts = pycompat.byteskwargs(opts)
415 opts = pycompat.byteskwargs(opts)
416 if not pats:
416 if not pats:
417 raise error.Abort(_(b'at least one filename or pattern is required'))
417 raise error.Abort(_(b'at least one filename or pattern is required'))
418
418
419 if opts.get(b'follow'):
419 if opts.get(b'follow'):
420 # --follow is deprecated and now just an alias for -f/--file
420 # --follow is deprecated and now just an alias for -f/--file
421 # to mimic the behavior of Mercurial before version 1.5
421 # to mimic the behavior of Mercurial before version 1.5
422 opts[b'file'] = True
422 opts[b'file'] = True
423
423
424 if (
424 if (
425 not opts.get(b'user')
425 not opts.get(b'user')
426 and not opts.get(b'changeset')
426 and not opts.get(b'changeset')
427 and not opts.get(b'date')
427 and not opts.get(b'date')
428 and not opts.get(b'file')
428 and not opts.get(b'file')
429 ):
429 ):
430 opts[b'number'] = True
430 opts[b'number'] = True
431
431
432 linenumber = opts.get(b'line_number') is not None
432 linenumber = opts.get(b'line_number') is not None
433 if (
433 if (
434 linenumber
434 linenumber
435 and (not opts.get(b'changeset'))
435 and (not opts.get(b'changeset'))
436 and (not opts.get(b'number'))
436 and (not opts.get(b'number'))
437 ):
437 ):
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439
439
440 rev = opts.get(b'rev')
440 rev = opts.get(b'rev')
441 if rev:
441 if rev:
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443 ctx = scmutil.revsingle(repo, rev)
443 ctx = scmutil.revsingle(repo, rev)
444
444
445 ui.pager(b'annotate')
445 ui.pager(b'annotate')
446 rootfm = ui.formatter(b'annotate', opts)
446 rootfm = ui.formatter(b'annotate', opts)
447 if ui.debugflag:
447 if ui.debugflag:
448 shorthex = pycompat.identity
448 shorthex = pycompat.identity
449 else:
449 else:
450
450
451 def shorthex(h):
451 def shorthex(h):
452 return h[:12]
452 return h[:12]
453
453
454 if ui.quiet:
454 if ui.quiet:
455 datefunc = dateutil.shortdate
455 datefunc = dateutil.shortdate
456 else:
456 else:
457 datefunc = dateutil.datestr
457 datefunc = dateutil.datestr
458 if ctx.rev() is None:
458 if ctx.rev() is None:
459 if opts.get(b'changeset'):
459 if opts.get(b'changeset'):
460 # omit "+" suffix which is appended to node hex
460 # omit "+" suffix which is appended to node hex
461 def formatrev(rev):
461 def formatrev(rev):
462 if rev == wdirrev:
462 if rev == wdirrev:
463 return b'%d' % ctx.p1().rev()
463 return b'%d' % ctx.p1().rev()
464 else:
464 else:
465 return b'%d' % rev
465 return b'%d' % rev
466
466
467 else:
467 else:
468
468
469 def formatrev(rev):
469 def formatrev(rev):
470 if rev == wdirrev:
470 if rev == wdirrev:
471 return b'%d+' % ctx.p1().rev()
471 return b'%d+' % ctx.p1().rev()
472 else:
472 else:
473 return b'%d ' % rev
473 return b'%d ' % rev
474
474
475 def formathex(h):
475 def formathex(h):
476 if h == wdirhex:
476 if h == wdirhex:
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
478 else:
478 else:
479 return b'%s ' % shorthex(h)
479 return b'%s ' % shorthex(h)
480
480
481 else:
481 else:
482 formatrev = b'%d'.__mod__
482 formatrev = b'%d'.__mod__
483 formathex = shorthex
483 formathex = shorthex
484
484
485 opmap = [
485 opmap = [
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 ]
492 ]
493 opnamemap = {
493 opnamemap = {
494 b'rev': b'number',
494 b'rev': b'number',
495 b'node': b'changeset',
495 b'node': b'changeset',
496 b'path': b'file',
496 b'path': b'file',
497 b'lineno': b'line_number',
497 b'lineno': b'line_number',
498 }
498 }
499
499
500 if rootfm.isplain():
500 if rootfm.isplain():
501
501
502 def makefunc(get, fmt):
502 def makefunc(get, fmt):
503 return lambda x: fmt(get(x))
503 return lambda x: fmt(get(x))
504
504
505 else:
505 else:
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return get
508 return get
509
509
510 datahint = rootfm.datahint()
510 datahint = rootfm.datahint()
511 funcmap = [
511 funcmap = [
512 (makefunc(get, fmt), sep)
512 (makefunc(get, fmt), sep)
513 for fn, sep, get, fmt in opmap
513 for fn, sep, get, fmt in opmap
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
515 ]
515 ]
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
517 fields = b' '.join(
517 fields = b' '.join(
518 fn
518 fn
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 )
521 )
522
522
523 def bad(x, y):
523 def bad(x, y):
524 raise error.Abort(b"%s: %s" % (x, y))
524 raise error.Abort(b"%s: %s" % (x, y))
525
525
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
527
527
528 follow = not opts.get(b'no_follow')
528 follow = not opts.get(b'no_follow')
529 diffopts = patch.difffeatureopts(
529 diffopts = patch.difffeatureopts(
530 ui, opts, section=b'annotate', whitespace=True
530 ui, opts, section=b'annotate', whitespace=True
531 )
531 )
532 skiprevs = opts.get(b'skip')
532 skiprevs = opts.get(b'skip')
533 if skiprevs:
533 if skiprevs:
534 skiprevs = scmutil.revrange(repo, skiprevs)
534 skiprevs = scmutil.revrange(repo, skiprevs)
535
535
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
537 for abs in ctx.walk(m):
537 for abs in ctx.walk(m):
538 fctx = ctx[abs]
538 fctx = ctx[abs]
539 rootfm.startitem()
539 rootfm.startitem()
540 rootfm.data(path=abs)
540 rootfm.data(path=abs)
541 if not opts.get(b'text') and fctx.isbinary():
541 if not opts.get(b'text') and fctx.isbinary():
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 continue
543 continue
544
544
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 lines = fctx.annotate(
546 lines = fctx.annotate(
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
548 )
548 )
549 if not lines:
549 if not lines:
550 fm.end()
550 fm.end()
551 continue
551 continue
552 formats = []
552 formats = []
553 pieces = []
553 pieces = []
554
554
555 for f, sep in funcmap:
555 for f, sep in funcmap:
556 l = [f(n) for n in lines]
556 l = [f(n) for n in lines]
557 if fm.isplain():
557 if fm.isplain():
558 sizes = [encoding.colwidth(x) for x in l]
558 sizes = [encoding.colwidth(x) for x in l]
559 ml = max(sizes)
559 ml = max(sizes)
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561 else:
561 else:
562 formats.append([b'%s' for x in l])
562 formats.append([b'%s' for x in l])
563 pieces.append(l)
563 pieces.append(l)
564
564
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566 fm.startitem()
566 fm.startitem()
567 fm.context(fctx=n.fctx)
567 fm.context(fctx=n.fctx)
568 fm.write(fields, b"".join(f), *p)
568 fm.write(fields, b"".join(f), *p)
569 if n.skip:
569 if n.skip:
570 fmt = b"* %s"
570 fmt = b"* %s"
571 else:
571 else:
572 fmt = b": %s"
572 fmt = b": %s"
573 fm.write(b'line', fmt, n.text)
573 fm.write(b'line', fmt, n.text)
574
574
575 if not lines[-1].text.endswith(b'\n'):
575 if not lines[-1].text.endswith(b'\n'):
576 fm.plain(b'\n')
576 fm.plain(b'\n')
577 fm.end()
577 fm.end()
578
578
579 rootfm.end()
579 rootfm.end()
580
580
581
581
582 @command(
582 @command(
583 b'archive',
583 b'archive',
584 [
584 [
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586 (
586 (
587 b'p',
587 b'p',
588 b'prefix',
588 b'prefix',
589 b'',
589 b'',
590 _(b'directory prefix for files in archive'),
590 _(b'directory prefix for files in archive'),
591 _(b'PREFIX'),
591 _(b'PREFIX'),
592 ),
592 ),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 ]
595 ]
596 + subrepoopts
596 + subrepoopts
597 + walkopts,
597 + walkopts,
598 _(b'[OPTION]... DEST'),
598 _(b'[OPTION]... DEST'),
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
600 )
600 )
601 def archive(ui, repo, dest, **opts):
601 def archive(ui, repo, dest, **opts):
602 '''create an unversioned archive of a repository revision
602 '''create an unversioned archive of a repository revision
603
603
604 By default, the revision used is the parent of the working
604 By default, the revision used is the parent of the working
605 directory; use -r/--rev to specify a different revision.
605 directory; use -r/--rev to specify a different revision.
606
606
607 The archive type is automatically detected based on file
607 The archive type is automatically detected based on file
608 extension (to override, use -t/--type).
608 extension (to override, use -t/--type).
609
609
610 .. container:: verbose
610 .. container:: verbose
611
611
612 Examples:
612 Examples:
613
613
614 - create a zip file containing the 1.0 release::
614 - create a zip file containing the 1.0 release::
615
615
616 hg archive -r 1.0 project-1.0.zip
616 hg archive -r 1.0 project-1.0.zip
617
617
618 - create a tarball excluding .hg files::
618 - create a tarball excluding .hg files::
619
619
620 hg archive project.tar.gz -X ".hg*"
620 hg archive project.tar.gz -X ".hg*"
621
621
622 Valid types are:
622 Valid types are:
623
623
624 :``files``: a directory full of files (default)
624 :``files``: a directory full of files (default)
625 :``tar``: tar archive, uncompressed
625 :``tar``: tar archive, uncompressed
626 :``tbz2``: tar archive, compressed using bzip2
626 :``tbz2``: tar archive, compressed using bzip2
627 :``tgz``: tar archive, compressed using gzip
627 :``tgz``: tar archive, compressed using gzip
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
629 :``uzip``: zip archive, uncompressed
629 :``uzip``: zip archive, uncompressed
630 :``zip``: zip archive, compressed using deflate
630 :``zip``: zip archive, compressed using deflate
631
631
632 The exact name of the destination archive or directory is given
632 The exact name of the destination archive or directory is given
633 using a format string; see :hg:`help export` for details.
633 using a format string; see :hg:`help export` for details.
634
634
635 Each member added to an archive file has a directory prefix
635 Each member added to an archive file has a directory prefix
636 prepended. Use -p/--prefix to specify a format string for the
636 prepended. Use -p/--prefix to specify a format string for the
637 prefix. The default is the basename of the archive, with suffixes
637 prefix. The default is the basename of the archive, with suffixes
638 removed.
638 removed.
639
639
640 Returns 0 on success.
640 Returns 0 on success.
641 '''
641 '''
642
642
643 opts = pycompat.byteskwargs(opts)
643 opts = pycompat.byteskwargs(opts)
644 rev = opts.get(b'rev')
644 rev = opts.get(b'rev')
645 if rev:
645 if rev:
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647 ctx = scmutil.revsingle(repo, rev)
647 ctx = scmutil.revsingle(repo, rev)
648 if not ctx:
648 if not ctx:
649 raise error.Abort(_(b'no working directory: please specify a revision'))
649 raise error.Abort(_(b'no working directory: please specify a revision'))
650 node = ctx.node()
650 node = ctx.node()
651 dest = cmdutil.makefilename(ctx, dest)
651 dest = cmdutil.makefilename(ctx, dest)
652 if os.path.realpath(dest) == repo.root:
652 if os.path.realpath(dest) == repo.root:
653 raise error.Abort(_(b'repository root cannot be destination'))
653 raise error.Abort(_(b'repository root cannot be destination'))
654
654
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
656 prefix = opts.get(b'prefix')
656 prefix = opts.get(b'prefix')
657
657
658 if dest == b'-':
658 if dest == b'-':
659 if kind == b'files':
659 if kind == b'files':
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
661 dest = cmdutil.makefileobj(ctx, dest)
661 dest = cmdutil.makefileobj(ctx, dest)
662 if not prefix:
662 if not prefix:
663 prefix = os.path.basename(repo.root) + b'-%h'
663 prefix = os.path.basename(repo.root) + b'-%h'
664
664
665 prefix = cmdutil.makefilename(ctx, prefix)
665 prefix = cmdutil.makefilename(ctx, prefix)
666 match = scmutil.match(ctx, [], opts)
666 match = scmutil.match(ctx, [], opts)
667 archival.archive(
667 archival.archive(
668 repo,
668 repo,
669 dest,
669 dest,
670 node,
670 node,
671 kind,
671 kind,
672 not opts.get(b'no_decode'),
672 not opts.get(b'no_decode'),
673 match,
673 match,
674 prefix,
674 prefix,
675 subrepos=opts.get(b'subrepos'),
675 subrepos=opts.get(b'subrepos'),
676 )
676 )
677
677
678
678
679 @command(
679 @command(
680 b'backout',
680 b'backout',
681 [
681 [
682 (
682 (
683 b'',
683 b'',
684 b'merge',
684 b'merge',
685 None,
685 None,
686 _(b'merge with old dirstate parent after backout'),
686 _(b'merge with old dirstate parent after backout'),
687 ),
687 ),
688 (
688 (
689 b'',
689 b'',
690 b'commit',
690 b'commit',
691 None,
691 None,
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
693 ),
693 ),
694 (b'', b'no-commit', None, _(b'do not commit')),
694 (b'', b'no-commit', None, _(b'do not commit')),
695 (
695 (
696 b'',
696 b'',
697 b'parent',
697 b'parent',
698 b'',
698 b'',
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
700 _(b'REV'),
700 _(b'REV'),
701 ),
701 ),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 ]
704 ]
705 + mergetoolopts
705 + mergetoolopts
706 + walkopts
706 + walkopts
707 + commitopts
707 + commitopts
708 + commitopts2,
708 + commitopts2,
709 _(b'[OPTION]... [-r] REV'),
709 _(b'[OPTION]... [-r] REV'),
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
711 )
711 )
712 def backout(ui, repo, node=None, rev=None, **opts):
712 def backout(ui, repo, node=None, rev=None, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Prepare a new changeset with the effect of REV undone in the
715 Prepare a new changeset with the effect of REV undone in the
716 current working directory. If no conflicts were encountered,
716 current working directory. If no conflicts were encountered,
717 it will be committed immediately.
717 it will be committed immediately.
718
718
719 If REV is the parent of the working directory, then this new changeset
719 If REV is the parent of the working directory, then this new changeset
720 is committed automatically (unless --no-commit is specified).
720 is committed automatically (unless --no-commit is specified).
721
721
722 .. note::
722 .. note::
723
723
724 :hg:`backout` cannot be used to fix either an unwanted or
724 :hg:`backout` cannot be used to fix either an unwanted or
725 incorrect merge.
725 incorrect merge.
726
726
727 .. container:: verbose
727 .. container:: verbose
728
728
729 Examples:
729 Examples:
730
730
731 - Reverse the effect of the parent of the working directory.
731 - Reverse the effect of the parent of the working directory.
732 This backout will be committed immediately::
732 This backout will be committed immediately::
733
733
734 hg backout -r .
734 hg backout -r .
735
735
736 - Reverse the effect of previous bad revision 23::
736 - Reverse the effect of previous bad revision 23::
737
737
738 hg backout -r 23
738 hg backout -r 23
739
739
740 - Reverse the effect of previous bad revision 23 and
740 - Reverse the effect of previous bad revision 23 and
741 leave changes uncommitted::
741 leave changes uncommitted::
742
742
743 hg backout -r 23 --no-commit
743 hg backout -r 23 --no-commit
744 hg commit -m "Backout revision 23"
744 hg commit -m "Backout revision 23"
745
745
746 By default, the pending changeset will have one parent,
746 By default, the pending changeset will have one parent,
747 maintaining a linear history. With --merge, the pending
747 maintaining a linear history. With --merge, the pending
748 changeset will instead have two parents: the old parent of the
748 changeset will instead have two parents: the old parent of the
749 working directory and a new child of REV that simply undoes REV.
749 working directory and a new child of REV that simply undoes REV.
750
750
751 Before version 1.7, the behavior without --merge was equivalent
751 Before version 1.7, the behavior without --merge was equivalent
752 to specifying --merge followed by :hg:`update --clean .` to
752 to specifying --merge followed by :hg:`update --clean .` to
753 cancel the merge and leave the child of REV as a head to be
753 cancel the merge and leave the child of REV as a head to be
754 merged separately.
754 merged separately.
755
755
756 See :hg:`help dates` for a list of formats valid for -d/--date.
756 See :hg:`help dates` for a list of formats valid for -d/--date.
757
757
758 See :hg:`help revert` for a way to restore files to the state
758 See :hg:`help revert` for a way to restore files to the state
759 of another revision.
759 of another revision.
760
760
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
762 files.
762 files.
763 '''
763 '''
764 with repo.wlock(), repo.lock():
764 with repo.wlock(), repo.lock():
765 return _dobackout(ui, repo, node, rev, **opts)
765 return _dobackout(ui, repo, node, rev, **opts)
766
766
767
767
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
769 opts = pycompat.byteskwargs(opts)
769 opts = pycompat.byteskwargs(opts)
770 if opts.get(b'commit') and opts.get(b'no_commit'):
770 if opts.get(b'commit') and opts.get(b'no_commit'):
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
772 if opts.get(b'merge') and opts.get(b'no_commit'):
772 if opts.get(b'merge') and opts.get(b'no_commit'):
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
774
774
775 if rev and node:
775 if rev and node:
776 raise error.Abort(_(b"please specify just one revision"))
776 raise error.Abort(_(b"please specify just one revision"))
777
777
778 if not rev:
778 if not rev:
779 rev = node
779 rev = node
780
780
781 if not rev:
781 if not rev:
782 raise error.Abort(_(b"please specify a revision to backout"))
782 raise error.Abort(_(b"please specify a revision to backout"))
783
783
784 date = opts.get(b'date')
784 date = opts.get(b'date')
785 if date:
785 if date:
786 opts[b'date'] = dateutil.parsedate(date)
786 opts[b'date'] = dateutil.parsedate(date)
787
787
788 cmdutil.checkunfinished(repo)
788 cmdutil.checkunfinished(repo)
789 cmdutil.bailifchanged(repo)
789 cmdutil.bailifchanged(repo)
790 node = scmutil.revsingle(repo, rev).node()
790 node = scmutil.revsingle(repo, rev).node()
791
791
792 op1, op2 = repo.dirstate.parents()
792 op1, op2 = repo.dirstate.parents()
793 if not repo.changelog.isancestor(node, op1):
793 if not repo.changelog.isancestor(node, op1):
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
795
795
796 p1, p2 = repo.changelog.parents(node)
796 p1, p2 = repo.changelog.parents(node)
797 if p1 == nullid:
797 if p1 == nullid:
798 raise error.Abort(_(b'cannot backout a change with no parents'))
798 raise error.Abort(_(b'cannot backout a change with no parents'))
799 if p2 != nullid:
799 if p2 != nullid:
800 if not opts.get(b'parent'):
800 if not opts.get(b'parent'):
801 raise error.Abort(_(b'cannot backout a merge changeset'))
801 raise error.Abort(_(b'cannot backout a merge changeset'))
802 p = repo.lookup(opts[b'parent'])
802 p = repo.lookup(opts[b'parent'])
803 if p not in (p1, p2):
803 if p not in (p1, p2):
804 raise error.Abort(
804 raise error.Abort(
805 _(b'%s is not a parent of %s') % (short(p), short(node))
805 _(b'%s is not a parent of %s') % (short(p), short(node))
806 )
806 )
807 parent = p
807 parent = p
808 else:
808 else:
809 if opts.get(b'parent'):
809 if opts.get(b'parent'):
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
811 parent = p1
811 parent = p1
812
812
813 # the backout should appear on the same branch
813 # the backout should appear on the same branch
814 branch = repo.dirstate.branch()
814 branch = repo.dirstate.branch()
815 bheads = repo.branchheads(branch)
815 bheads = repo.branchheads(branch)
816 rctx = scmutil.revsingle(repo, hex(parent))
816 rctx = scmutil.revsingle(repo, hex(parent))
817 if not opts.get(b'merge') and op1 != node:
817 if not opts.get(b'merge') and op1 != node:
818 with dirstateguard.dirstateguard(repo, b'backout'):
818 with dirstateguard.dirstateguard(repo, b'backout'):
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
820 with ui.configoverride(overrides, b'backout'):
820 with ui.configoverride(overrides, b'backout'):
821 stats = mergemod.update(
821 stats = mergemod.update(
822 repo,
822 repo,
823 parent,
823 parent,
824 branchmerge=True,
824 branchmerge=True,
825 force=True,
825 force=True,
826 ancestor=node,
826 ancestor=node,
827 mergeancestor=False,
827 mergeancestor=False,
828 )
828 )
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
860 if not newnode:
860 if not newnode:
861 ui.status(_(b"nothing changed\n"))
861 ui.status(_(b"nothing changed\n"))
862 return 1
862 return 1
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
864
864
865 def nice(node):
865 def nice(node):
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
867
867
868 ui.status(
868 ui.status(
869 _(b'changeset %s backs out changeset %s\n')
869 _(b'changeset %s backs out changeset %s\n')
870 % (nice(repo.changelog.tip()), nice(node))
870 % (nice(repo.changelog.tip()), nice(node))
871 )
871 )
872 if opts.get(b'merge') and op1 != node:
872 if opts.get(b'merge') and op1 != node:
873 hg.clean(repo, op1, show_stats=False)
873 hg.clean(repo, op1, show_stats=False)
874 ui.status(
874 ui.status(
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
876 )
876 )
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 with ui.configoverride(overrides, b'backout'):
878 with ui.configoverride(overrides, b'backout'):
879 return hg.merge(repo, hex(repo.changelog.tip()))
879 return hg.merge(repo, hex(repo.changelog.tip()))
880 return 0
880 return 0
881
881
882
882
883 @command(
883 @command(
884 b'bisect',
884 b'bisect',
885 [
885 [
886 (b'r', b'reset', False, _(b'reset bisect state')),
886 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'g', b'good', False, _(b'mark changeset good')),
887 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (
891 (
892 b'c',
892 b'c',
893 b'command',
893 b'command',
894 b'',
894 b'',
895 _(b'use command to check changeset state'),
895 _(b'use command to check changeset state'),
896 _(b'CMD'),
896 _(b'CMD'),
897 ),
897 ),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
899 ],
899 ],
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 )
902 )
903 def bisect(
903 def bisect(
904 ui,
904 ui,
905 repo,
905 repo,
906 rev=None,
906 rev=None,
907 extra=None,
907 extra=None,
908 command=None,
908 command=None,
909 reset=None,
909 reset=None,
910 good=None,
910 good=None,
911 bad=None,
911 bad=None,
912 skip=None,
912 skip=None,
913 extend=None,
913 extend=None,
914 noupdate=None,
914 noupdate=None,
915 ):
915 ):
916 """subdivision search of changesets
916 """subdivision search of changesets
917
917
918 This command helps to find changesets which introduce problems. To
918 This command helps to find changesets which introduce problems. To
919 use, mark the earliest changeset you know exhibits the problem as
919 use, mark the earliest changeset you know exhibits the problem as
920 bad, then mark the latest changeset which is free from the problem
920 bad, then mark the latest changeset which is free from the problem
921 as good. Bisect will update your working directory to a revision
921 as good. Bisect will update your working directory to a revision
922 for testing (unless the -U/--noupdate option is specified). Once
922 for testing (unless the -U/--noupdate option is specified). Once
923 you have performed tests, mark the working directory as good or
923 you have performed tests, mark the working directory as good or
924 bad, and bisect will either update to another candidate changeset
924 bad, and bisect will either update to another candidate changeset
925 or announce that it has found the bad revision.
925 or announce that it has found the bad revision.
926
926
927 As a shortcut, you can also use the revision argument to mark a
927 As a shortcut, you can also use the revision argument to mark a
928 revision as good or bad without checking it out first.
928 revision as good or bad without checking it out first.
929
929
930 If you supply a command, it will be used for automatic bisection.
930 If you supply a command, it will be used for automatic bisection.
931 The environment variable HG_NODE will contain the ID of the
931 The environment variable HG_NODE will contain the ID of the
932 changeset being tested. The exit status of the command will be
932 changeset being tested. The exit status of the command will be
933 used to mark revisions as good or bad: status 0 means good, 125
933 used to mark revisions as good or bad: status 0 means good, 125
934 means to skip the revision, 127 (command not found) will abort the
934 means to skip the revision, 127 (command not found) will abort the
935 bisection, and any other non-zero exit status means the revision
935 bisection, and any other non-zero exit status means the revision
936 is bad.
936 is bad.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Some examples:
940 Some examples:
941
941
942 - start a bisection with known bad revision 34, and good revision 12::
942 - start a bisection with known bad revision 34, and good revision 12::
943
943
944 hg bisect --bad 34
944 hg bisect --bad 34
945 hg bisect --good 12
945 hg bisect --good 12
946
946
947 - advance the current bisection by marking current revision as good or
947 - advance the current bisection by marking current revision as good or
948 bad::
948 bad::
949
949
950 hg bisect --good
950 hg bisect --good
951 hg bisect --bad
951 hg bisect --bad
952
952
953 - mark the current revision, or a known revision, to be skipped (e.g. if
953 - mark the current revision, or a known revision, to be skipped (e.g. if
954 that revision is not usable because of another issue)::
954 that revision is not usable because of another issue)::
955
955
956 hg bisect --skip
956 hg bisect --skip
957 hg bisect --skip 23
957 hg bisect --skip 23
958
958
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960
960
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962
962
963 - forget the current bisection::
963 - forget the current bisection::
964
964
965 hg bisect --reset
965 hg bisect --reset
966
966
967 - use 'make && make tests' to automatically find the first broken
967 - use 'make && make tests' to automatically find the first broken
968 revision::
968 revision::
969
969
970 hg bisect --reset
970 hg bisect --reset
971 hg bisect --bad 34
971 hg bisect --bad 34
972 hg bisect --good 12
972 hg bisect --good 12
973 hg bisect --command "make && make tests"
973 hg bisect --command "make && make tests"
974
974
975 - see all changesets whose states are already known in the current
975 - see all changesets whose states are already known in the current
976 bisection::
976 bisection::
977
977
978 hg log -r "bisect(pruned)"
978 hg log -r "bisect(pruned)"
979
979
980 - see the changeset currently being bisected (especially useful
980 - see the changeset currently being bisected (especially useful
981 if running with -U/--noupdate)::
981 if running with -U/--noupdate)::
982
982
983 hg log -r "bisect(current)"
983 hg log -r "bisect(current)"
984
984
985 - see all changesets that took part in the current bisection::
985 - see all changesets that took part in the current bisection::
986
986
987 hg log -r "bisect(range)"
987 hg log -r "bisect(range)"
988
988
989 - you can even get a nice graph::
989 - you can even get a nice graph::
990
990
991 hg log --graph -r "bisect(range)"
991 hg log --graph -r "bisect(range)"
992
992
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994
994
995 Returns 0 on success.
995 Returns 0 on success.
996 """
996 """
997 # backward compatibility
997 # backward compatibility
998 if rev in b"good bad reset init".split():
998 if rev in b"good bad reset init".split():
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1000 cmd, rev, extra = rev, extra, None
1000 cmd, rev, extra = rev, extra, None
1001 if cmd == b"good":
1001 if cmd == b"good":
1002 good = True
1002 good = True
1003 elif cmd == b"bad":
1003 elif cmd == b"bad":
1004 bad = True
1004 bad = True
1005 else:
1005 else:
1006 reset = True
1006 reset = True
1007 elif extra:
1007 elif extra:
1008 raise error.Abort(_(b'incompatible arguments'))
1008 raise error.Abort(_(b'incompatible arguments'))
1009
1009
1010 incompatibles = {
1010 incompatibles = {
1011 b'--bad': bad,
1011 b'--bad': bad,
1012 b'--command': bool(command),
1012 b'--command': bool(command),
1013 b'--extend': extend,
1013 b'--extend': extend,
1014 b'--good': good,
1014 b'--good': good,
1015 b'--reset': reset,
1015 b'--reset': reset,
1016 b'--skip': skip,
1016 b'--skip': skip,
1017 }
1017 }
1018
1018
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1020
1020
1021 if len(enabled) > 1:
1021 if len(enabled) > 1:
1022 raise error.Abort(
1022 raise error.Abort(
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1024 )
1024 )
1025
1025
1026 if reset:
1026 if reset:
1027 hbisect.resetstate(repo)
1027 hbisect.resetstate(repo)
1028 return
1028 return
1029
1029
1030 state = hbisect.load_state(repo)
1030 state = hbisect.load_state(repo)
1031
1031
1032 # update state
1032 # update state
1033 if good or bad or skip:
1033 if good or bad or skip:
1034 if rev:
1034 if rev:
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1036 else:
1036 else:
1037 nodes = [repo.lookup(b'.')]
1037 nodes = [repo.lookup(b'.')]
1038 if good:
1038 if good:
1039 state[b'good'] += nodes
1039 state[b'good'] += nodes
1040 elif bad:
1040 elif bad:
1041 state[b'bad'] += nodes
1041 state[b'bad'] += nodes
1042 elif skip:
1042 elif skip:
1043 state[b'skip'] += nodes
1043 state[b'skip'] += nodes
1044 hbisect.save_state(repo, state)
1044 hbisect.save_state(repo, state)
1045 if not (state[b'good'] and state[b'bad']):
1045 if not (state[b'good'] and state[b'bad']):
1046 return
1046 return
1047
1047
1048 def mayupdate(repo, node, show_stats=True):
1048 def mayupdate(repo, node, show_stats=True):
1049 """common used update sequence"""
1049 """common used update sequence"""
1050 if noupdate:
1050 if noupdate:
1051 return
1051 return
1052 cmdutil.checkunfinished(repo)
1052 cmdutil.checkunfinished(repo)
1053 cmdutil.bailifchanged(repo)
1053 cmdutil.bailifchanged(repo)
1054 return hg.clean(repo, node, show_stats=show_stats)
1054 return hg.clean(repo, node, show_stats=show_stats)
1055
1055
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1057
1057
1058 if command:
1058 if command:
1059 changesets = 1
1059 changesets = 1
1060 if noupdate:
1060 if noupdate:
1061 try:
1061 try:
1062 node = state[b'current'][0]
1062 node = state[b'current'][0]
1063 except LookupError:
1063 except LookupError:
1064 raise error.Abort(
1064 raise error.Abort(
1065 _(
1065 _(
1066 b'current bisect revision is unknown - '
1066 b'current bisect revision is unknown - '
1067 b'start a new bisect to fix'
1067 b'start a new bisect to fix'
1068 )
1068 )
1069 )
1069 )
1070 else:
1070 else:
1071 node, p2 = repo.dirstate.parents()
1071 node, p2 = repo.dirstate.parents()
1072 if p2 != nullid:
1072 if p2 != nullid:
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1074 if rev:
1074 if rev:
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1076 with hbisect.restore_state(repo, state, node):
1076 with hbisect.restore_state(repo, state, node):
1077 while changesets:
1077 while changesets:
1078 # update state
1078 # update state
1079 state[b'current'] = [node]
1079 state[b'current'] = [node]
1080 hbisect.save_state(repo, state)
1080 hbisect.save_state(repo, state)
1081 status = ui.system(
1081 status = ui.system(
1082 command,
1082 command,
1083 environ={b'HG_NODE': hex(node)},
1083 environ={b'HG_NODE': hex(node)},
1084 blockedtag=b'bisect_check',
1084 blockedtag=b'bisect_check',
1085 )
1085 )
1086 if status == 125:
1086 if status == 125:
1087 transition = b"skip"
1087 transition = b"skip"
1088 elif status == 0:
1088 elif status == 0:
1089 transition = b"good"
1089 transition = b"good"
1090 # status < 0 means process was killed
1090 # status < 0 means process was killed
1091 elif status == 127:
1091 elif status == 127:
1092 raise error.Abort(_(b"failed to execute %s") % command)
1092 raise error.Abort(_(b"failed to execute %s") % command)
1093 elif status < 0:
1093 elif status < 0:
1094 raise error.Abort(_(b"%s killed") % command)
1094 raise error.Abort(_(b"%s killed") % command)
1095 else:
1095 else:
1096 transition = b"bad"
1096 transition = b"bad"
1097 state[transition].append(node)
1097 state[transition].append(node)
1098 ctx = repo[node]
1098 ctx = repo[node]
1099 ui.status(
1099 ui.status(
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1101 )
1101 )
1102 hbisect.checkstate(state)
1102 hbisect.checkstate(state)
1103 # bisect
1103 # bisect
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1105 # update to next check
1105 # update to next check
1106 node = nodes[0]
1106 node = nodes[0]
1107 mayupdate(repo, node, show_stats=False)
1107 mayupdate(repo, node, show_stats=False)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1109 return
1109 return
1110
1110
1111 hbisect.checkstate(state)
1111 hbisect.checkstate(state)
1112
1112
1113 # actually bisect
1113 # actually bisect
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1115 if extend:
1115 if extend:
1116 if not changesets:
1116 if not changesets:
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1118 if extendnode is not None:
1118 if extendnode is not None:
1119 ui.write(
1119 ui.write(
1120 _(b"Extending search to changeset %d:%s\n")
1120 _(b"Extending search to changeset %d:%s\n")
1121 % (extendnode.rev(), extendnode)
1121 % (extendnode.rev(), extendnode)
1122 )
1122 )
1123 state[b'current'] = [extendnode.node()]
1123 state[b'current'] = [extendnode.node()]
1124 hbisect.save_state(repo, state)
1124 hbisect.save_state(repo, state)
1125 return mayupdate(repo, extendnode.node())
1125 return mayupdate(repo, extendnode.node())
1126 raise error.Abort(_(b"nothing to extend"))
1126 raise error.Abort(_(b"nothing to extend"))
1127
1127
1128 if changesets == 0:
1128 if changesets == 0:
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1130 else:
1130 else:
1131 assert len(nodes) == 1 # only a single node can be tested next
1131 assert len(nodes) == 1 # only a single node can be tested next
1132 node = nodes[0]
1132 node = nodes[0]
1133 # compute the approximate number of remaining tests
1133 # compute the approximate number of remaining tests
1134 tests, size = 0, 2
1134 tests, size = 0, 2
1135 while size <= changesets:
1135 while size <= changesets:
1136 tests, size = tests + 1, size * 2
1136 tests, size = tests + 1, size * 2
1137 rev = repo.changelog.rev(node)
1137 rev = repo.changelog.rev(node)
1138 ui.write(
1138 ui.write(
1139 _(
1139 _(
1140 b"Testing changeset %d:%s "
1140 b"Testing changeset %d:%s "
1141 b"(%d changesets remaining, ~%d tests)\n"
1141 b"(%d changesets remaining, ~%d tests)\n"
1142 )
1142 )
1143 % (rev, short(node), changesets, tests)
1143 % (rev, short(node), changesets, tests)
1144 )
1144 )
1145 state[b'current'] = [node]
1145 state[b'current'] = [node]
1146 hbisect.save_state(repo, state)
1146 hbisect.save_state(repo, state)
1147 return mayupdate(repo, node)
1147 return mayupdate(repo, node)
1148
1148
1149
1149
1150 @command(
1150 @command(
1151 b'bookmarks|bookmark',
1151 b'bookmarks|bookmark',
1152 [
1152 [
1153 (b'f', b'force', False, _(b'force')),
1153 (b'f', b'force', False, _(b'force')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1159 ]
1159 ]
1160 + formatteropts,
1160 + formatteropts,
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1163 )
1163 )
1164 def bookmark(ui, repo, *names, **opts):
1164 def bookmark(ui, repo, *names, **opts):
1165 '''create a new bookmark or list existing bookmarks
1165 '''create a new bookmark or list existing bookmarks
1166
1166
1167 Bookmarks are labels on changesets to help track lines of development.
1167 Bookmarks are labels on changesets to help track lines of development.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1170
1170
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1172 The active bookmark is indicated with a '*'.
1172 The active bookmark is indicated with a '*'.
1173 When a commit is made, the active bookmark will advance to the new commit.
1173 When a commit is made, the active bookmark will advance to the new commit.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1175 Updating away from a bookmark will cause it to be deactivated.
1175 Updating away from a bookmark will cause it to be deactivated.
1176
1176
1177 Bookmarks can be pushed and pulled between repositories (see
1177 Bookmarks can be pushed and pulled between repositories (see
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1180 be created. Using :hg:`merge` will resolve the divergence.
1180 be created. Using :hg:`merge` will resolve the divergence.
1181
1181
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1183 the active bookmark's name.
1183 the active bookmark's name.
1184
1184
1185 A bookmark named '@' has the special property that :hg:`clone` will
1185 A bookmark named '@' has the special property that :hg:`clone` will
1186 check it out by default if it exists.
1186 check it out by default if it exists.
1187
1187
1188 .. container:: verbose
1188 .. container:: verbose
1189
1189
1190 Template:
1190 Template:
1191
1191
1192 The following keywords are supported in addition to the common template
1192 The following keywords are supported in addition to the common template
1193 keywords and functions such as ``{bookmark}``. See also
1193 keywords and functions such as ``{bookmark}``. See also
1194 :hg:`help templates`.
1194 :hg:`help templates`.
1195
1195
1196 :active: Boolean. True if the bookmark is active.
1196 :active: Boolean. True if the bookmark is active.
1197
1197
1198 Examples:
1198 Examples:
1199
1199
1200 - create an active bookmark for a new line of development::
1200 - create an active bookmark for a new line of development::
1201
1201
1202 hg book new-feature
1202 hg book new-feature
1203
1203
1204 - create an inactive bookmark as a place marker::
1204 - create an inactive bookmark as a place marker::
1205
1205
1206 hg book -i reviewed
1206 hg book -i reviewed
1207
1207
1208 - create an inactive bookmark on another changeset::
1208 - create an inactive bookmark on another changeset::
1209
1209
1210 hg book -r .^ tested
1210 hg book -r .^ tested
1211
1211
1212 - rename bookmark turkey to dinner::
1212 - rename bookmark turkey to dinner::
1213
1213
1214 hg book -m turkey dinner
1214 hg book -m turkey dinner
1215
1215
1216 - move the '@' bookmark from another branch::
1216 - move the '@' bookmark from another branch::
1217
1217
1218 hg book -f @
1218 hg book -f @
1219
1219
1220 - print only the active bookmark name::
1220 - print only the active bookmark name::
1221
1221
1222 hg book -ql .
1222 hg book -ql .
1223 '''
1223 '''
1224 opts = pycompat.byteskwargs(opts)
1224 opts = pycompat.byteskwargs(opts)
1225 force = opts.get(b'force')
1225 force = opts.get(b'force')
1226 rev = opts.get(b'rev')
1226 rev = opts.get(b'rev')
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1228
1228
1229 selactions = [k for k in [b'delete', b'rename', b'list'] if opts.get(k)]
1229 selactions = [k for k in [b'delete', b'rename', b'list'] if opts.get(k)]
1230 if len(selactions) > 1:
1230 if len(selactions) > 1:
1231 raise error.Abort(
1231 raise error.Abort(
1232 _(b'--%s and --%s are incompatible') % tuple(selactions[:2])
1232 _(b'--%s and --%s are incompatible') % tuple(selactions[:2])
1233 )
1233 )
1234 if selactions:
1234 if selactions:
1235 action = selactions[0]
1235 action = selactions[0]
1236 elif names or rev:
1236 elif names or rev:
1237 action = b'add'
1237 action = b'add'
1238 elif inactive:
1238 elif inactive:
1239 action = b'inactive' # meaning deactivate
1239 action = b'inactive' # meaning deactivate
1240 else:
1240 else:
1241 action = b'list'
1241 action = b'list'
1242
1242
1243 if rev and action in {b'delete', b'rename', b'list'}:
1243 if rev and action in {b'delete', b'rename', b'list'}:
1244 raise error.Abort(_(b"--rev is incompatible with --%s") % action)
1244 raise error.Abort(_(b"--rev is incompatible with --%s") % action)
1245 if inactive and action in {b'delete', b'list'}:
1245 if inactive and action in {b'delete', b'list'}:
1246 raise error.Abort(_(b"--inactive is incompatible with --%s") % action)
1246 raise error.Abort(_(b"--inactive is incompatible with --%s") % action)
1247 if not names and action in {b'add', b'delete'}:
1247 if not names and action in {b'add', b'delete'}:
1248 raise error.Abort(_(b"bookmark name required"))
1248 raise error.Abort(_(b"bookmark name required"))
1249
1249
1250 if action in {b'add', b'delete', b'rename', b'inactive'}:
1250 if action in {b'add', b'delete', b'rename', b'inactive'}:
1251 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1251 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1252 if action == b'delete':
1252 if action == b'delete':
1253 names = pycompat.maplist(repo._bookmarks.expandname, names)
1253 names = pycompat.maplist(repo._bookmarks.expandname, names)
1254 bookmarks.delete(repo, tr, names)
1254 bookmarks.delete(repo, tr, names)
1255 elif action == b'rename':
1255 elif action == b'rename':
1256 if not names:
1256 if not names:
1257 raise error.Abort(_(b"new bookmark name required"))
1257 raise error.Abort(_(b"new bookmark name required"))
1258 elif len(names) > 1:
1258 elif len(names) > 1:
1259 raise error.Abort(_(b"only one new bookmark name allowed"))
1259 raise error.Abort(_(b"only one new bookmark name allowed"))
1260 oldname = repo._bookmarks.expandname(opts[b'rename'])
1260 oldname = repo._bookmarks.expandname(opts[b'rename'])
1261 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1261 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1262 elif action == b'add':
1262 elif action == b'add':
1263 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1263 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1264 elif action == b'inactive':
1264 elif action == b'inactive':
1265 if len(repo._bookmarks) == 0:
1265 if len(repo._bookmarks) == 0:
1266 ui.status(_(b"no bookmarks set\n"))
1266 ui.status(_(b"no bookmarks set\n"))
1267 elif not repo._activebookmark:
1267 elif not repo._activebookmark:
1268 ui.status(_(b"no active bookmark\n"))
1268 ui.status(_(b"no active bookmark\n"))
1269 else:
1269 else:
1270 bookmarks.deactivate(repo)
1270 bookmarks.deactivate(repo)
1271 elif action == b'list':
1271 elif action == b'list':
1272 names = pycompat.maplist(repo._bookmarks.expandname, names)
1272 names = pycompat.maplist(repo._bookmarks.expandname, names)
1273 with ui.formatter(b'bookmarks', opts) as fm:
1273 with ui.formatter(b'bookmarks', opts) as fm:
1274 bookmarks.printbookmarks(ui, repo, fm, names)
1274 bookmarks.printbookmarks(ui, repo, fm, names)
1275 else:
1275 else:
1276 raise error.ProgrammingError(b'invalid action: %s' % action)
1276 raise error.ProgrammingError(b'invalid action: %s' % action)
1277
1277
1278
1278
1279 @command(
1279 @command(
1280 b'branch',
1280 b'branch',
1281 [
1281 [
1282 (
1282 (
1283 b'f',
1283 b'f',
1284 b'force',
1284 b'force',
1285 None,
1285 None,
1286 _(b'set branch name even if it shadows an existing branch'),
1286 _(b'set branch name even if it shadows an existing branch'),
1287 ),
1287 ),
1288 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1288 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1289 (
1289 (
1290 b'r',
1290 b'r',
1291 b'rev',
1291 b'rev',
1292 [],
1292 [],
1293 _(b'change branches of the given revs (EXPERIMENTAL)'),
1293 _(b'change branches of the given revs (EXPERIMENTAL)'),
1294 ),
1294 ),
1295 ],
1295 ],
1296 _(b'[-fC] [NAME]'),
1296 _(b'[-fC] [NAME]'),
1297 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1297 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1298 )
1298 )
1299 def branch(ui, repo, label=None, **opts):
1299 def branch(ui, repo, label=None, **opts):
1300 """set or show the current branch name
1300 """set or show the current branch name
1301
1301
1302 .. note::
1302 .. note::
1303
1303
1304 Branch names are permanent and global. Use :hg:`bookmark` to create a
1304 Branch names are permanent and global. Use :hg:`bookmark` to create a
1305 light-weight bookmark instead. See :hg:`help glossary` for more
1305 light-weight bookmark instead. See :hg:`help glossary` for more
1306 information about named branches and bookmarks.
1306 information about named branches and bookmarks.
1307
1307
1308 With no argument, show the current branch name. With one argument,
1308 With no argument, show the current branch name. With one argument,
1309 set the working directory branch name (the branch will not exist
1309 set the working directory branch name (the branch will not exist
1310 in the repository until the next commit). Standard practice
1310 in the repository until the next commit). Standard practice
1311 recommends that primary development take place on the 'default'
1311 recommends that primary development take place on the 'default'
1312 branch.
1312 branch.
1313
1313
1314 Unless -f/--force is specified, branch will not let you set a
1314 Unless -f/--force is specified, branch will not let you set a
1315 branch name that already exists.
1315 branch name that already exists.
1316
1316
1317 Use -C/--clean to reset the working directory branch to that of
1317 Use -C/--clean to reset the working directory branch to that of
1318 the parent of the working directory, negating a previous branch
1318 the parent of the working directory, negating a previous branch
1319 change.
1319 change.
1320
1320
1321 Use the command :hg:`update` to switch to an existing branch. Use
1321 Use the command :hg:`update` to switch to an existing branch. Use
1322 :hg:`commit --close-branch` to mark this branch head as closed.
1322 :hg:`commit --close-branch` to mark this branch head as closed.
1323 When all heads of a branch are closed, the branch will be
1323 When all heads of a branch are closed, the branch will be
1324 considered closed.
1324 considered closed.
1325
1325
1326 Returns 0 on success.
1326 Returns 0 on success.
1327 """
1327 """
1328 opts = pycompat.byteskwargs(opts)
1328 opts = pycompat.byteskwargs(opts)
1329 revs = opts.get(b'rev')
1329 revs = opts.get(b'rev')
1330 if label:
1330 if label:
1331 label = label.strip()
1331 label = label.strip()
1332
1332
1333 if not opts.get(b'clean') and not label:
1333 if not opts.get(b'clean') and not label:
1334 if revs:
1334 if revs:
1335 raise error.Abort(_(b"no branch name specified for the revisions"))
1335 raise error.Abort(_(b"no branch name specified for the revisions"))
1336 ui.write(b"%s\n" % repo.dirstate.branch())
1336 ui.write(b"%s\n" % repo.dirstate.branch())
1337 return
1337 return
1338
1338
1339 with repo.wlock():
1339 with repo.wlock():
1340 if opts.get(b'clean'):
1340 if opts.get(b'clean'):
1341 label = repo[b'.'].branch()
1341 label = repo[b'.'].branch()
1342 repo.dirstate.setbranch(label)
1342 repo.dirstate.setbranch(label)
1343 ui.status(_(b'reset working directory to branch %s\n') % label)
1343 ui.status(_(b'reset working directory to branch %s\n') % label)
1344 elif label:
1344 elif label:
1345
1345
1346 scmutil.checknewlabel(repo, label, b'branch')
1346 scmutil.checknewlabel(repo, label, b'branch')
1347 if revs:
1347 if revs:
1348 return cmdutil.changebranch(ui, repo, revs, label)
1348 return cmdutil.changebranch(ui, repo, revs, label)
1349
1349
1350 if not opts.get(b'force') and label in repo.branchmap():
1350 if not opts.get(b'force') and label in repo.branchmap():
1351 if label not in [p.branch() for p in repo[None].parents()]:
1351 if label not in [p.branch() for p in repo[None].parents()]:
1352 raise error.Abort(
1352 raise error.Abort(
1353 _(b'a branch of the same name already exists'),
1353 _(b'a branch of the same name already exists'),
1354 # i18n: "it" refers to an existing branch
1354 # i18n: "it" refers to an existing branch
1355 hint=_(b"use 'hg update' to switch to it"),
1355 hint=_(b"use 'hg update' to switch to it"),
1356 )
1356 )
1357
1357
1358 repo.dirstate.setbranch(label)
1358 repo.dirstate.setbranch(label)
1359 ui.status(_(b'marked working directory as branch %s\n') % label)
1359 ui.status(_(b'marked working directory as branch %s\n') % label)
1360
1360
1361 # find any open named branches aside from default
1361 # find any open named branches aside from default
1362 for n, h, t, c in repo.branchmap().iterbranches():
1362 for n, h, t, c in repo.branchmap().iterbranches():
1363 if n != b"default" and not c:
1363 if n != b"default" and not c:
1364 return 0
1364 return 0
1365 ui.status(
1365 ui.status(
1366 _(
1366 _(
1367 b'(branches are permanent and global, '
1367 b'(branches are permanent and global, '
1368 b'did you want a bookmark?)\n'
1368 b'did you want a bookmark?)\n'
1369 )
1369 )
1370 )
1370 )
1371
1371
1372
1372
1373 @command(
1373 @command(
1374 b'branches',
1374 b'branches',
1375 [
1375 [
1376 (
1376 (
1377 b'a',
1377 b'a',
1378 b'active',
1378 b'active',
1379 False,
1379 False,
1380 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1380 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1381 ),
1381 ),
1382 (b'c', b'closed', False, _(b'show normal and closed branches')),
1382 (b'c', b'closed', False, _(b'show normal and closed branches')),
1383 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1383 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1384 ]
1384 ]
1385 + formatteropts,
1385 + formatteropts,
1386 _(b'[-c]'),
1386 _(b'[-c]'),
1387 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1387 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1388 intents={INTENT_READONLY},
1388 intents={INTENT_READONLY},
1389 )
1389 )
1390 def branches(ui, repo, active=False, closed=False, **opts):
1390 def branches(ui, repo, active=False, closed=False, **opts):
1391 """list repository named branches
1391 """list repository named branches
1392
1392
1393 List the repository's named branches, indicating which ones are
1393 List the repository's named branches, indicating which ones are
1394 inactive. If -c/--closed is specified, also list branches which have
1394 inactive. If -c/--closed is specified, also list branches which have
1395 been marked closed (see :hg:`commit --close-branch`).
1395 been marked closed (see :hg:`commit --close-branch`).
1396
1396
1397 Use the command :hg:`update` to switch to an existing branch.
1397 Use the command :hg:`update` to switch to an existing branch.
1398
1398
1399 .. container:: verbose
1399 .. container:: verbose
1400
1400
1401 Template:
1401 Template:
1402
1402
1403 The following keywords are supported in addition to the common template
1403 The following keywords are supported in addition to the common template
1404 keywords and functions such as ``{branch}``. See also
1404 keywords and functions such as ``{branch}``. See also
1405 :hg:`help templates`.
1405 :hg:`help templates`.
1406
1406
1407 :active: Boolean. True if the branch is active.
1407 :active: Boolean. True if the branch is active.
1408 :closed: Boolean. True if the branch is closed.
1408 :closed: Boolean. True if the branch is closed.
1409 :current: Boolean. True if it is the current branch.
1409 :current: Boolean. True if it is the current branch.
1410
1410
1411 Returns 0.
1411 Returns 0.
1412 """
1412 """
1413
1413
1414 opts = pycompat.byteskwargs(opts)
1414 opts = pycompat.byteskwargs(opts)
1415 revs = opts.get(b'rev')
1415 revs = opts.get(b'rev')
1416 selectedbranches = None
1416 selectedbranches = None
1417 if revs:
1417 if revs:
1418 revs = scmutil.revrange(repo, revs)
1418 revs = scmutil.revrange(repo, revs)
1419 getbi = repo.revbranchcache().branchinfo
1419 getbi = repo.revbranchcache().branchinfo
1420 selectedbranches = {getbi(r)[0] for r in revs}
1420 selectedbranches = {getbi(r)[0] for r in revs}
1421
1421
1422 ui.pager(b'branches')
1422 ui.pager(b'branches')
1423 fm = ui.formatter(b'branches', opts)
1423 fm = ui.formatter(b'branches', opts)
1424 hexfunc = fm.hexfunc
1424 hexfunc = fm.hexfunc
1425
1425
1426 allheads = set(repo.heads())
1426 allheads = set(repo.heads())
1427 branches = []
1427 branches = []
1428 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1428 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1429 if selectedbranches is not None and tag not in selectedbranches:
1429 if selectedbranches is not None and tag not in selectedbranches:
1430 continue
1430 continue
1431 isactive = False
1431 isactive = False
1432 if not isclosed:
1432 if not isclosed:
1433 openheads = set(repo.branchmap().iteropen(heads))
1433 openheads = set(repo.branchmap().iteropen(heads))
1434 isactive = bool(openheads & allheads)
1434 isactive = bool(openheads & allheads)
1435 branches.append((tag, repo[tip], isactive, not isclosed))
1435 branches.append((tag, repo[tip], isactive, not isclosed))
1436 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1436 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1437
1437
1438 for tag, ctx, isactive, isopen in branches:
1438 for tag, ctx, isactive, isopen in branches:
1439 if active and not isactive:
1439 if active and not isactive:
1440 continue
1440 continue
1441 if isactive:
1441 if isactive:
1442 label = b'branches.active'
1442 label = b'branches.active'
1443 notice = b''
1443 notice = b''
1444 elif not isopen:
1444 elif not isopen:
1445 if not closed:
1445 if not closed:
1446 continue
1446 continue
1447 label = b'branches.closed'
1447 label = b'branches.closed'
1448 notice = _(b' (closed)')
1448 notice = _(b' (closed)')
1449 else:
1449 else:
1450 label = b'branches.inactive'
1450 label = b'branches.inactive'
1451 notice = _(b' (inactive)')
1451 notice = _(b' (inactive)')
1452 current = tag == repo.dirstate.branch()
1452 current = tag == repo.dirstate.branch()
1453 if current:
1453 if current:
1454 label = b'branches.current'
1454 label = b'branches.current'
1455
1455
1456 fm.startitem()
1456 fm.startitem()
1457 fm.write(b'branch', b'%s', tag, label=label)
1457 fm.write(b'branch', b'%s', tag, label=label)
1458 rev = ctx.rev()
1458 rev = ctx.rev()
1459 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1459 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1460 fmt = b' ' * padsize + b' %d:%s'
1460 fmt = b' ' * padsize + b' %d:%s'
1461 fm.condwrite(
1461 fm.condwrite(
1462 not ui.quiet,
1462 not ui.quiet,
1463 b'rev node',
1463 b'rev node',
1464 fmt,
1464 fmt,
1465 rev,
1465 rev,
1466 hexfunc(ctx.node()),
1466 hexfunc(ctx.node()),
1467 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1467 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1468 )
1468 )
1469 fm.context(ctx=ctx)
1469 fm.context(ctx=ctx)
1470 fm.data(active=isactive, closed=not isopen, current=current)
1470 fm.data(active=isactive, closed=not isopen, current=current)
1471 if not ui.quiet:
1471 if not ui.quiet:
1472 fm.plain(notice)
1472 fm.plain(notice)
1473 fm.plain(b'\n')
1473 fm.plain(b'\n')
1474 fm.end()
1474 fm.end()
1475
1475
1476
1476
1477 @command(
1477 @command(
1478 b'bundle',
1478 b'bundle',
1479 [
1479 [
1480 (
1480 (
1481 b'f',
1481 b'f',
1482 b'force',
1482 b'force',
1483 None,
1483 None,
1484 _(b'run even when the destination is unrelated'),
1484 _(b'run even when the destination is unrelated'),
1485 ),
1485 ),
1486 (
1486 (
1487 b'r',
1487 b'r',
1488 b'rev',
1488 b'rev',
1489 [],
1489 [],
1490 _(b'a changeset intended to be added to the destination'),
1490 _(b'a changeset intended to be added to the destination'),
1491 _(b'REV'),
1491 _(b'REV'),
1492 ),
1492 ),
1493 (
1493 (
1494 b'b',
1494 b'b',
1495 b'branch',
1495 b'branch',
1496 [],
1496 [],
1497 _(b'a specific branch you would like to bundle'),
1497 _(b'a specific branch you would like to bundle'),
1498 _(b'BRANCH'),
1498 _(b'BRANCH'),
1499 ),
1499 ),
1500 (
1500 (
1501 b'',
1501 b'',
1502 b'base',
1502 b'base',
1503 [],
1503 [],
1504 _(b'a base changeset assumed to be available at the destination'),
1504 _(b'a base changeset assumed to be available at the destination'),
1505 _(b'REV'),
1505 _(b'REV'),
1506 ),
1506 ),
1507 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1507 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1508 (
1508 (
1509 b't',
1509 b't',
1510 b'type',
1510 b'type',
1511 b'bzip2',
1511 b'bzip2',
1512 _(b'bundle compression type to use'),
1512 _(b'bundle compression type to use'),
1513 _(b'TYPE'),
1513 _(b'TYPE'),
1514 ),
1514 ),
1515 ]
1515 ]
1516 + remoteopts,
1516 + remoteopts,
1517 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1517 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1518 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1518 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1519 )
1519 )
1520 def bundle(ui, repo, fname, dest=None, **opts):
1520 def bundle(ui, repo, fname, dest=None, **opts):
1521 """create a bundle file
1521 """create a bundle file
1522
1522
1523 Generate a bundle file containing data to be transferred to another
1523 Generate a bundle file containing data to be transferred to another
1524 repository.
1524 repository.
1525
1525
1526 To create a bundle containing all changesets, use -a/--all
1526 To create a bundle containing all changesets, use -a/--all
1527 (or --base null). Otherwise, hg assumes the destination will have
1527 (or --base null). Otherwise, hg assumes the destination will have
1528 all the nodes you specify with --base parameters. Otherwise, hg
1528 all the nodes you specify with --base parameters. Otherwise, hg
1529 will assume the repository has all the nodes in destination, or
1529 will assume the repository has all the nodes in destination, or
1530 default-push/default if no destination is specified, where destination
1530 default-push/default if no destination is specified, where destination
1531 is the repository you provide through DEST option.
1531 is the repository you provide through DEST option.
1532
1532
1533 You can change bundle format with the -t/--type option. See
1533 You can change bundle format with the -t/--type option. See
1534 :hg:`help bundlespec` for documentation on this format. By default,
1534 :hg:`help bundlespec` for documentation on this format. By default,
1535 the most appropriate format is used and compression defaults to
1535 the most appropriate format is used and compression defaults to
1536 bzip2.
1536 bzip2.
1537
1537
1538 The bundle file can then be transferred using conventional means
1538 The bundle file can then be transferred using conventional means
1539 and applied to another repository with the unbundle or pull
1539 and applied to another repository with the unbundle or pull
1540 command. This is useful when direct push and pull are not
1540 command. This is useful when direct push and pull are not
1541 available or when exporting an entire repository is undesirable.
1541 available or when exporting an entire repository is undesirable.
1542
1542
1543 Applying bundles preserves all changeset contents including
1543 Applying bundles preserves all changeset contents including
1544 permissions, copy/rename information, and revision history.
1544 permissions, copy/rename information, and revision history.
1545
1545
1546 Returns 0 on success, 1 if no changes found.
1546 Returns 0 on success, 1 if no changes found.
1547 """
1547 """
1548 opts = pycompat.byteskwargs(opts)
1548 opts = pycompat.byteskwargs(opts)
1549 revs = None
1549 revs = None
1550 if b'rev' in opts:
1550 if b'rev' in opts:
1551 revstrings = opts[b'rev']
1551 revstrings = opts[b'rev']
1552 revs = scmutil.revrange(repo, revstrings)
1552 revs = scmutil.revrange(repo, revstrings)
1553 if revstrings and not revs:
1553 if revstrings and not revs:
1554 raise error.Abort(_(b'no commits to bundle'))
1554 raise error.Abort(_(b'no commits to bundle'))
1555
1555
1556 bundletype = opts.get(b'type', b'bzip2').lower()
1556 bundletype = opts.get(b'type', b'bzip2').lower()
1557 try:
1557 try:
1558 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1558 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1559 except error.UnsupportedBundleSpecification as e:
1559 except error.UnsupportedBundleSpecification as e:
1560 raise error.Abort(
1560 raise error.Abort(
1561 pycompat.bytestr(e),
1561 pycompat.bytestr(e),
1562 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1562 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1563 )
1563 )
1564 cgversion = bundlespec.contentopts[b"cg.version"]
1564 cgversion = bundlespec.contentopts[b"cg.version"]
1565
1565
1566 # Packed bundles are a pseudo bundle format for now.
1566 # Packed bundles are a pseudo bundle format for now.
1567 if cgversion == b's1':
1567 if cgversion == b's1':
1568 raise error.Abort(
1568 raise error.Abort(
1569 _(b'packed bundles cannot be produced by "hg bundle"'),
1569 _(b'packed bundles cannot be produced by "hg bundle"'),
1570 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1570 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1571 )
1571 )
1572
1572
1573 if opts.get(b'all'):
1573 if opts.get(b'all'):
1574 if dest:
1574 if dest:
1575 raise error.Abort(
1575 raise error.Abort(
1576 _(b"--all is incompatible with specifying a destination")
1576 _(b"--all is incompatible with specifying a destination")
1577 )
1577 )
1578 if opts.get(b'base'):
1578 if opts.get(b'base'):
1579 ui.warn(_(b"ignoring --base because --all was specified\n"))
1579 ui.warn(_(b"ignoring --base because --all was specified\n"))
1580 base = [nullrev]
1580 base = [nullrev]
1581 else:
1581 else:
1582 base = scmutil.revrange(repo, opts.get(b'base'))
1582 base = scmutil.revrange(repo, opts.get(b'base'))
1583 if cgversion not in changegroup.supportedoutgoingversions(repo):
1583 if cgversion not in changegroup.supportedoutgoingversions(repo):
1584 raise error.Abort(
1584 raise error.Abort(
1585 _(b"repository does not support bundle version %s") % cgversion
1585 _(b"repository does not support bundle version %s") % cgversion
1586 )
1586 )
1587
1587
1588 if base:
1588 if base:
1589 if dest:
1589 if dest:
1590 raise error.Abort(
1590 raise error.Abort(
1591 _(b"--base is incompatible with specifying a destination")
1591 _(b"--base is incompatible with specifying a destination")
1592 )
1592 )
1593 common = [repo[rev].node() for rev in base]
1593 common = [repo[rev].node() for rev in base]
1594 heads = [repo[r].node() for r in revs] if revs else None
1594 heads = [repo[r].node() for r in revs] if revs else None
1595 outgoing = discovery.outgoing(repo, common, heads)
1595 outgoing = discovery.outgoing(repo, common, heads)
1596 else:
1596 else:
1597 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1597 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1598 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1598 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1599 other = hg.peer(repo, opts, dest)
1599 other = hg.peer(repo, opts, dest)
1600 revs = [repo[r].hex() for r in revs]
1600 revs = [repo[r].hex() for r in revs]
1601 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1601 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1602 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1602 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1603 outgoing = discovery.findcommonoutgoing(
1603 outgoing = discovery.findcommonoutgoing(
1604 repo,
1604 repo,
1605 other,
1605 other,
1606 onlyheads=heads,
1606 onlyheads=heads,
1607 force=opts.get(b'force'),
1607 force=opts.get(b'force'),
1608 portable=True,
1608 portable=True,
1609 )
1609 )
1610
1610
1611 if not outgoing.missing:
1611 if not outgoing.missing:
1612 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1612 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1613 return 1
1613 return 1
1614
1614
1615 if cgversion == b'01': # bundle1
1615 if cgversion == b'01': # bundle1
1616 bversion = b'HG10' + bundlespec.wirecompression
1616 bversion = b'HG10' + bundlespec.wirecompression
1617 bcompression = None
1617 bcompression = None
1618 elif cgversion in (b'02', b'03'):
1618 elif cgversion in (b'02', b'03'):
1619 bversion = b'HG20'
1619 bversion = b'HG20'
1620 bcompression = bundlespec.wirecompression
1620 bcompression = bundlespec.wirecompression
1621 else:
1621 else:
1622 raise error.ProgrammingError(
1622 raise error.ProgrammingError(
1623 b'bundle: unexpected changegroup version %s' % cgversion
1623 b'bundle: unexpected changegroup version %s' % cgversion
1624 )
1624 )
1625
1625
1626 # TODO compression options should be derived from bundlespec parsing.
1626 # TODO compression options should be derived from bundlespec parsing.
1627 # This is a temporary hack to allow adjusting bundle compression
1627 # This is a temporary hack to allow adjusting bundle compression
1628 # level without a) formalizing the bundlespec changes to declare it
1628 # level without a) formalizing the bundlespec changes to declare it
1629 # b) introducing a command flag.
1629 # b) introducing a command flag.
1630 compopts = {}
1630 compopts = {}
1631 complevel = ui.configint(
1631 complevel = ui.configint(
1632 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1632 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1633 )
1633 )
1634 if complevel is None:
1634 if complevel is None:
1635 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1635 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1636 if complevel is not None:
1636 if complevel is not None:
1637 compopts[b'level'] = complevel
1637 compopts[b'level'] = complevel
1638
1638
1639 # Allow overriding the bundling of obsmarker in phases through
1639 # Allow overriding the bundling of obsmarker in phases through
1640 # configuration while we don't have a bundle version that include them
1640 # configuration while we don't have a bundle version that include them
1641 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1641 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1642 bundlespec.contentopts[b'obsolescence'] = True
1642 bundlespec.contentopts[b'obsolescence'] = True
1643 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1643 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1644 bundlespec.contentopts[b'phases'] = True
1644 bundlespec.contentopts[b'phases'] = True
1645
1645
1646 bundle2.writenewbundle(
1646 bundle2.writenewbundle(
1647 ui,
1647 ui,
1648 repo,
1648 repo,
1649 b'bundle',
1649 b'bundle',
1650 fname,
1650 fname,
1651 bversion,
1651 bversion,
1652 outgoing,
1652 outgoing,
1653 bundlespec.contentopts,
1653 bundlespec.contentopts,
1654 compression=bcompression,
1654 compression=bcompression,
1655 compopts=compopts,
1655 compopts=compopts,
1656 )
1656 )
1657
1657
1658
1658
1659 @command(
1659 @command(
1660 b'cat',
1660 b'cat',
1661 [
1661 [
1662 (
1662 (
1663 b'o',
1663 b'o',
1664 b'output',
1664 b'output',
1665 b'',
1665 b'',
1666 _(b'print output to file with formatted name'),
1666 _(b'print output to file with formatted name'),
1667 _(b'FORMAT'),
1667 _(b'FORMAT'),
1668 ),
1668 ),
1669 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1669 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1670 (b'', b'decode', None, _(b'apply any matching decode filter')),
1670 (b'', b'decode', None, _(b'apply any matching decode filter')),
1671 ]
1671 ]
1672 + walkopts
1672 + walkopts
1673 + formatteropts,
1673 + formatteropts,
1674 _(b'[OPTION]... FILE...'),
1674 _(b'[OPTION]... FILE...'),
1675 helpcategory=command.CATEGORY_FILE_CONTENTS,
1675 helpcategory=command.CATEGORY_FILE_CONTENTS,
1676 inferrepo=True,
1676 inferrepo=True,
1677 intents={INTENT_READONLY},
1677 intents={INTENT_READONLY},
1678 )
1678 )
1679 def cat(ui, repo, file1, *pats, **opts):
1679 def cat(ui, repo, file1, *pats, **opts):
1680 """output the current or given revision of files
1680 """output the current or given revision of files
1681
1681
1682 Print the specified files as they were at the given revision. If
1682 Print the specified files as they were at the given revision. If
1683 no revision is given, the parent of the working directory is used.
1683 no revision is given, the parent of the working directory is used.
1684
1684
1685 Output may be to a file, in which case the name of the file is
1685 Output may be to a file, in which case the name of the file is
1686 given using a template string. See :hg:`help templates`. In addition
1686 given using a template string. See :hg:`help templates`. In addition
1687 to the common template keywords, the following formatting rules are
1687 to the common template keywords, the following formatting rules are
1688 supported:
1688 supported:
1689
1689
1690 :``%%``: literal "%" character
1690 :``%%``: literal "%" character
1691 :``%s``: basename of file being printed
1691 :``%s``: basename of file being printed
1692 :``%d``: dirname of file being printed, or '.' if in repository root
1692 :``%d``: dirname of file being printed, or '.' if in repository root
1693 :``%p``: root-relative path name of file being printed
1693 :``%p``: root-relative path name of file being printed
1694 :``%H``: changeset hash (40 hexadecimal digits)
1694 :``%H``: changeset hash (40 hexadecimal digits)
1695 :``%R``: changeset revision number
1695 :``%R``: changeset revision number
1696 :``%h``: short-form changeset hash (12 hexadecimal digits)
1696 :``%h``: short-form changeset hash (12 hexadecimal digits)
1697 :``%r``: zero-padded changeset revision number
1697 :``%r``: zero-padded changeset revision number
1698 :``%b``: basename of the exporting repository
1698 :``%b``: basename of the exporting repository
1699 :``\\``: literal "\\" character
1699 :``\\``: literal "\\" character
1700
1700
1701 .. container:: verbose
1701 .. container:: verbose
1702
1702
1703 Template:
1703 Template:
1704
1704
1705 The following keywords are supported in addition to the common template
1705 The following keywords are supported in addition to the common template
1706 keywords and functions. See also :hg:`help templates`.
1706 keywords and functions. See also :hg:`help templates`.
1707
1707
1708 :data: String. File content.
1708 :data: String. File content.
1709 :path: String. Repository-absolute path of the file.
1709 :path: String. Repository-absolute path of the file.
1710
1710
1711 Returns 0 on success.
1711 Returns 0 on success.
1712 """
1712 """
1713 opts = pycompat.byteskwargs(opts)
1713 opts = pycompat.byteskwargs(opts)
1714 rev = opts.get(b'rev')
1714 rev = opts.get(b'rev')
1715 if rev:
1715 if rev:
1716 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1716 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1717 ctx = scmutil.revsingle(repo, rev)
1717 ctx = scmutil.revsingle(repo, rev)
1718 m = scmutil.match(ctx, (file1,) + pats, opts)
1718 m = scmutil.match(ctx, (file1,) + pats, opts)
1719 fntemplate = opts.pop(b'output', b'')
1719 fntemplate = opts.pop(b'output', b'')
1720 if cmdutil.isstdiofilename(fntemplate):
1720 if cmdutil.isstdiofilename(fntemplate):
1721 fntemplate = b''
1721 fntemplate = b''
1722
1722
1723 if fntemplate:
1723 if fntemplate:
1724 fm = formatter.nullformatter(ui, b'cat', opts)
1724 fm = formatter.nullformatter(ui, b'cat', opts)
1725 else:
1725 else:
1726 ui.pager(b'cat')
1726 ui.pager(b'cat')
1727 fm = ui.formatter(b'cat', opts)
1727 fm = ui.formatter(b'cat', opts)
1728 with fm:
1728 with fm:
1729 return cmdutil.cat(
1729 return cmdutil.cat(
1730 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1730 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1731 )
1731 )
1732
1732
1733
1733
1734 @command(
1734 @command(
1735 b'clone',
1735 b'clone',
1736 [
1736 [
1737 (
1737 (
1738 b'U',
1738 b'U',
1739 b'noupdate',
1739 b'noupdate',
1740 None,
1740 None,
1741 _(
1741 _(
1742 b'the clone will include an empty working '
1742 b'the clone will include an empty working '
1743 b'directory (only a repository)'
1743 b'directory (only a repository)'
1744 ),
1744 ),
1745 ),
1745 ),
1746 (
1746 (
1747 b'u',
1747 b'u',
1748 b'updaterev',
1748 b'updaterev',
1749 b'',
1749 b'',
1750 _(b'revision, tag, or branch to check out'),
1750 _(b'revision, tag, or branch to check out'),
1751 _(b'REV'),
1751 _(b'REV'),
1752 ),
1752 ),
1753 (
1753 (
1754 b'r',
1754 b'r',
1755 b'rev',
1755 b'rev',
1756 [],
1756 [],
1757 _(
1757 _(
1758 b'do not clone everything, but include this changeset'
1758 b'do not clone everything, but include this changeset'
1759 b' and its ancestors'
1759 b' and its ancestors'
1760 ),
1760 ),
1761 _(b'REV'),
1761 _(b'REV'),
1762 ),
1762 ),
1763 (
1763 (
1764 b'b',
1764 b'b',
1765 b'branch',
1765 b'branch',
1766 [],
1766 [],
1767 _(
1767 _(
1768 b'do not clone everything, but include this branch\'s'
1768 b'do not clone everything, but include this branch\'s'
1769 b' changesets and their ancestors'
1769 b' changesets and their ancestors'
1770 ),
1770 ),
1771 _(b'BRANCH'),
1771 _(b'BRANCH'),
1772 ),
1772 ),
1773 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1773 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1774 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1774 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1775 (b'', b'stream', None, _(b'clone with minimal data processing')),
1775 (b'', b'stream', None, _(b'clone with minimal data processing')),
1776 ]
1776 ]
1777 + remoteopts,
1777 + remoteopts,
1778 _(b'[OPTION]... SOURCE [DEST]'),
1778 _(b'[OPTION]... SOURCE [DEST]'),
1779 helpcategory=command.CATEGORY_REPO_CREATION,
1779 helpcategory=command.CATEGORY_REPO_CREATION,
1780 helpbasic=True,
1780 helpbasic=True,
1781 norepo=True,
1781 norepo=True,
1782 )
1782 )
1783 def clone(ui, source, dest=None, **opts):
1783 def clone(ui, source, dest=None, **opts):
1784 """make a copy of an existing repository
1784 """make a copy of an existing repository
1785
1785
1786 Create a copy of an existing repository in a new directory.
1786 Create a copy of an existing repository in a new directory.
1787
1787
1788 If no destination directory name is specified, it defaults to the
1788 If no destination directory name is specified, it defaults to the
1789 basename of the source.
1789 basename of the source.
1790
1790
1791 The location of the source is added to the new repository's
1791 The location of the source is added to the new repository's
1792 ``.hg/hgrc`` file, as the default to be used for future pulls.
1792 ``.hg/hgrc`` file, as the default to be used for future pulls.
1793
1793
1794 Only local paths and ``ssh://`` URLs are supported as
1794 Only local paths and ``ssh://`` URLs are supported as
1795 destinations. For ``ssh://`` destinations, no working directory or
1795 destinations. For ``ssh://`` destinations, no working directory or
1796 ``.hg/hgrc`` will be created on the remote side.
1796 ``.hg/hgrc`` will be created on the remote side.
1797
1797
1798 If the source repository has a bookmark called '@' set, that
1798 If the source repository has a bookmark called '@' set, that
1799 revision will be checked out in the new repository by default.
1799 revision will be checked out in the new repository by default.
1800
1800
1801 To check out a particular version, use -u/--update, or
1801 To check out a particular version, use -u/--update, or
1802 -U/--noupdate to create a clone with no working directory.
1802 -U/--noupdate to create a clone with no working directory.
1803
1803
1804 To pull only a subset of changesets, specify one or more revisions
1804 To pull only a subset of changesets, specify one or more revisions
1805 identifiers with -r/--rev or branches with -b/--branch. The
1805 identifiers with -r/--rev or branches with -b/--branch. The
1806 resulting clone will contain only the specified changesets and
1806 resulting clone will contain only the specified changesets and
1807 their ancestors. These options (or 'clone src#rev dest') imply
1807 their ancestors. These options (or 'clone src#rev dest') imply
1808 --pull, even for local source repositories.
1808 --pull, even for local source repositories.
1809
1809
1810 In normal clone mode, the remote normalizes repository data into a common
1810 In normal clone mode, the remote normalizes repository data into a common
1811 exchange format and the receiving end translates this data into its local
1811 exchange format and the receiving end translates this data into its local
1812 storage format. --stream activates a different clone mode that essentially
1812 storage format. --stream activates a different clone mode that essentially
1813 copies repository files from the remote with minimal data processing. This
1813 copies repository files from the remote with minimal data processing. This
1814 significantly reduces the CPU cost of a clone both remotely and locally.
1814 significantly reduces the CPU cost of a clone both remotely and locally.
1815 However, it often increases the transferred data size by 30-40%. This can
1815 However, it often increases the transferred data size by 30-40%. This can
1816 result in substantially faster clones where I/O throughput is plentiful,
1816 result in substantially faster clones where I/O throughput is plentiful,
1817 especially for larger repositories. A side-effect of --stream clones is
1817 especially for larger repositories. A side-effect of --stream clones is
1818 that storage settings and requirements on the remote are applied locally:
1818 that storage settings and requirements on the remote are applied locally:
1819 a modern client may inherit legacy or inefficient storage used by the
1819 a modern client may inherit legacy or inefficient storage used by the
1820 remote or a legacy Mercurial client may not be able to clone from a
1820 remote or a legacy Mercurial client may not be able to clone from a
1821 modern Mercurial remote.
1821 modern Mercurial remote.
1822
1822
1823 .. note::
1823 .. note::
1824
1824
1825 Specifying a tag will include the tagged changeset but not the
1825 Specifying a tag will include the tagged changeset but not the
1826 changeset containing the tag.
1826 changeset containing the tag.
1827
1827
1828 .. container:: verbose
1828 .. container:: verbose
1829
1829
1830 For efficiency, hardlinks are used for cloning whenever the
1830 For efficiency, hardlinks are used for cloning whenever the
1831 source and destination are on the same filesystem (note this
1831 source and destination are on the same filesystem (note this
1832 applies only to the repository data, not to the working
1832 applies only to the repository data, not to the working
1833 directory). Some filesystems, such as AFS, implement hardlinking
1833 directory). Some filesystems, such as AFS, implement hardlinking
1834 incorrectly, but do not report errors. In these cases, use the
1834 incorrectly, but do not report errors. In these cases, use the
1835 --pull option to avoid hardlinking.
1835 --pull option to avoid hardlinking.
1836
1836
1837 Mercurial will update the working directory to the first applicable
1837 Mercurial will update the working directory to the first applicable
1838 revision from this list:
1838 revision from this list:
1839
1839
1840 a) null if -U or the source repository has no changesets
1840 a) null if -U or the source repository has no changesets
1841 b) if -u . and the source repository is local, the first parent of
1841 b) if -u . and the source repository is local, the first parent of
1842 the source repository's working directory
1842 the source repository's working directory
1843 c) the changeset specified with -u (if a branch name, this means the
1843 c) the changeset specified with -u (if a branch name, this means the
1844 latest head of that branch)
1844 latest head of that branch)
1845 d) the changeset specified with -r
1845 d) the changeset specified with -r
1846 e) the tipmost head specified with -b
1846 e) the tipmost head specified with -b
1847 f) the tipmost head specified with the url#branch source syntax
1847 f) the tipmost head specified with the url#branch source syntax
1848 g) the revision marked with the '@' bookmark, if present
1848 g) the revision marked with the '@' bookmark, if present
1849 h) the tipmost head of the default branch
1849 h) the tipmost head of the default branch
1850 i) tip
1850 i) tip
1851
1851
1852 When cloning from servers that support it, Mercurial may fetch
1852 When cloning from servers that support it, Mercurial may fetch
1853 pre-generated data from a server-advertised URL or inline from the
1853 pre-generated data from a server-advertised URL or inline from the
1854 same stream. When this is done, hooks operating on incoming changesets
1854 same stream. When this is done, hooks operating on incoming changesets
1855 and changegroups may fire more than once, once for each pre-generated
1855 and changegroups may fire more than once, once for each pre-generated
1856 bundle and as well as for any additional remaining data. In addition,
1856 bundle and as well as for any additional remaining data. In addition,
1857 if an error occurs, the repository may be rolled back to a partial
1857 if an error occurs, the repository may be rolled back to a partial
1858 clone. This behavior may change in future releases.
1858 clone. This behavior may change in future releases.
1859 See :hg:`help -e clonebundles` for more.
1859 See :hg:`help -e clonebundles` for more.
1860
1860
1861 Examples:
1861 Examples:
1862
1862
1863 - clone a remote repository to a new directory named hg/::
1863 - clone a remote repository to a new directory named hg/::
1864
1864
1865 hg clone https://www.mercurial-scm.org/repo/hg/
1865 hg clone https://www.mercurial-scm.org/repo/hg/
1866
1866
1867 - create a lightweight local clone::
1867 - create a lightweight local clone::
1868
1868
1869 hg clone project/ project-feature/
1869 hg clone project/ project-feature/
1870
1870
1871 - clone from an absolute path on an ssh server (note double-slash)::
1871 - clone from an absolute path on an ssh server (note double-slash)::
1872
1872
1873 hg clone ssh://user@server//home/projects/alpha/
1873 hg clone ssh://user@server//home/projects/alpha/
1874
1874
1875 - do a streaming clone while checking out a specified version::
1875 - do a streaming clone while checking out a specified version::
1876
1876
1877 hg clone --stream http://server/repo -u 1.5
1877 hg clone --stream http://server/repo -u 1.5
1878
1878
1879 - create a repository without changesets after a particular revision::
1879 - create a repository without changesets after a particular revision::
1880
1880
1881 hg clone -r 04e544 experimental/ good/
1881 hg clone -r 04e544 experimental/ good/
1882
1882
1883 - clone (and track) a particular named branch::
1883 - clone (and track) a particular named branch::
1884
1884
1885 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1885 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1886
1886
1887 See :hg:`help urls` for details on specifying URLs.
1887 See :hg:`help urls` for details on specifying URLs.
1888
1888
1889 Returns 0 on success.
1889 Returns 0 on success.
1890 """
1890 """
1891 opts = pycompat.byteskwargs(opts)
1891 opts = pycompat.byteskwargs(opts)
1892 if opts.get(b'noupdate') and opts.get(b'updaterev'):
1892 if opts.get(b'noupdate') and opts.get(b'updaterev'):
1893 raise error.Abort(_(b"cannot specify both --noupdate and --updaterev"))
1893 raise error.Abort(_(b"cannot specify both --noupdate and --updaterev"))
1894
1894
1895 # --include/--exclude can come from narrow or sparse.
1895 # --include/--exclude can come from narrow or sparse.
1896 includepats, excludepats = None, None
1896 includepats, excludepats = None, None
1897
1897
1898 # hg.clone() differentiates between None and an empty set. So make sure
1898 # hg.clone() differentiates between None and an empty set. So make sure
1899 # patterns are sets if narrow is requested without patterns.
1899 # patterns are sets if narrow is requested without patterns.
1900 if opts.get(b'narrow'):
1900 if opts.get(b'narrow'):
1901 includepats = set()
1901 includepats = set()
1902 excludepats = set()
1902 excludepats = set()
1903
1903
1904 if opts.get(b'include'):
1904 if opts.get(b'include'):
1905 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1905 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1906 if opts.get(b'exclude'):
1906 if opts.get(b'exclude'):
1907 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1907 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1908
1908
1909 r = hg.clone(
1909 r = hg.clone(
1910 ui,
1910 ui,
1911 opts,
1911 opts,
1912 source,
1912 source,
1913 dest,
1913 dest,
1914 pull=opts.get(b'pull'),
1914 pull=opts.get(b'pull'),
1915 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1915 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1916 revs=opts.get(b'rev'),
1916 revs=opts.get(b'rev'),
1917 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1917 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1918 branch=opts.get(b'branch'),
1918 branch=opts.get(b'branch'),
1919 shareopts=opts.get(b'shareopts'),
1919 shareopts=opts.get(b'shareopts'),
1920 storeincludepats=includepats,
1920 storeincludepats=includepats,
1921 storeexcludepats=excludepats,
1921 storeexcludepats=excludepats,
1922 depth=opts.get(b'depth') or None,
1922 depth=opts.get(b'depth') or None,
1923 )
1923 )
1924
1924
1925 return r is None
1925 return r is None
1926
1926
1927
1927
1928 @command(
1928 @command(
1929 b'commit|ci',
1929 b'commit|ci',
1930 [
1930 [
1931 (
1931 (
1932 b'A',
1932 b'A',
1933 b'addremove',
1933 b'addremove',
1934 None,
1934 None,
1935 _(b'mark new/missing files as added/removed before committing'),
1935 _(b'mark new/missing files as added/removed before committing'),
1936 ),
1936 ),
1937 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1937 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1938 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1938 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1939 (b's', b'secret', None, _(b'use the secret phase for committing')),
1939 (b's', b'secret', None, _(b'use the secret phase for committing')),
1940 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1940 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1941 (
1941 (
1942 b'',
1942 b'',
1943 b'force-close-branch',
1943 b'force-close-branch',
1944 None,
1944 None,
1945 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1945 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1946 ),
1946 ),
1947 (b'i', b'interactive', None, _(b'use interactive mode')),
1947 (b'i', b'interactive', None, _(b'use interactive mode')),
1948 ]
1948 ]
1949 + walkopts
1949 + walkopts
1950 + commitopts
1950 + commitopts
1951 + commitopts2
1951 + commitopts2
1952 + subrepoopts,
1952 + subrepoopts,
1953 _(b'[OPTION]... [FILE]...'),
1953 _(b'[OPTION]... [FILE]...'),
1954 helpcategory=command.CATEGORY_COMMITTING,
1954 helpcategory=command.CATEGORY_COMMITTING,
1955 helpbasic=True,
1955 helpbasic=True,
1956 inferrepo=True,
1956 inferrepo=True,
1957 )
1957 )
1958 def commit(ui, repo, *pats, **opts):
1958 def commit(ui, repo, *pats, **opts):
1959 """commit the specified files or all outstanding changes
1959 """commit the specified files or all outstanding changes
1960
1960
1961 Commit changes to the given files into the repository. Unlike a
1961 Commit changes to the given files into the repository. Unlike a
1962 centralized SCM, this operation is a local operation. See
1962 centralized SCM, this operation is a local operation. See
1963 :hg:`push` for a way to actively distribute your changes.
1963 :hg:`push` for a way to actively distribute your changes.
1964
1964
1965 If a list of files is omitted, all changes reported by :hg:`status`
1965 If a list of files is omitted, all changes reported by :hg:`status`
1966 will be committed.
1966 will be committed.
1967
1967
1968 If you are committing the result of a merge, do not provide any
1968 If you are committing the result of a merge, do not provide any
1969 filenames or -I/-X filters.
1969 filenames or -I/-X filters.
1970
1970
1971 If no commit message is specified, Mercurial starts your
1971 If no commit message is specified, Mercurial starts your
1972 configured editor where you can enter a message. In case your
1972 configured editor where you can enter a message. In case your
1973 commit fails, you will find a backup of your message in
1973 commit fails, you will find a backup of your message in
1974 ``.hg/last-message.txt``.
1974 ``.hg/last-message.txt``.
1975
1975
1976 The --close-branch flag can be used to mark the current branch
1976 The --close-branch flag can be used to mark the current branch
1977 head closed. When all heads of a branch are closed, the branch
1977 head closed. When all heads of a branch are closed, the branch
1978 will be considered closed and no longer listed.
1978 will be considered closed and no longer listed.
1979
1979
1980 The --amend flag can be used to amend the parent of the
1980 The --amend flag can be used to amend the parent of the
1981 working directory with a new commit that contains the changes
1981 working directory with a new commit that contains the changes
1982 in the parent in addition to those currently reported by :hg:`status`,
1982 in the parent in addition to those currently reported by :hg:`status`,
1983 if there are any. The old commit is stored in a backup bundle in
1983 if there are any. The old commit is stored in a backup bundle in
1984 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1984 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1985 on how to restore it).
1985 on how to restore it).
1986
1986
1987 Message, user and date are taken from the amended commit unless
1987 Message, user and date are taken from the amended commit unless
1988 specified. When a message isn't specified on the command line,
1988 specified. When a message isn't specified on the command line,
1989 the editor will open with the message of the amended commit.
1989 the editor will open with the message of the amended commit.
1990
1990
1991 It is not possible to amend public changesets (see :hg:`help phases`)
1991 It is not possible to amend public changesets (see :hg:`help phases`)
1992 or changesets that have children.
1992 or changesets that have children.
1993
1993
1994 See :hg:`help dates` for a list of formats valid for -d/--date.
1994 See :hg:`help dates` for a list of formats valid for -d/--date.
1995
1995
1996 Returns 0 on success, 1 if nothing changed.
1996 Returns 0 on success, 1 if nothing changed.
1997
1997
1998 .. container:: verbose
1998 .. container:: verbose
1999
1999
2000 Examples:
2000 Examples:
2001
2001
2002 - commit all files ending in .py::
2002 - commit all files ending in .py::
2003
2003
2004 hg commit --include "set:**.py"
2004 hg commit --include "set:**.py"
2005
2005
2006 - commit all non-binary files::
2006 - commit all non-binary files::
2007
2007
2008 hg commit --exclude "set:binary()"
2008 hg commit --exclude "set:binary()"
2009
2009
2010 - amend the current commit and set the date to now::
2010 - amend the current commit and set the date to now::
2011
2011
2012 hg commit --amend --date now
2012 hg commit --amend --date now
2013 """
2013 """
2014 with repo.wlock(), repo.lock():
2014 with repo.wlock(), repo.lock():
2015 return _docommit(ui, repo, *pats, **opts)
2015 return _docommit(ui, repo, *pats, **opts)
2016
2016
2017
2017
2018 def _docommit(ui, repo, *pats, **opts):
2018 def _docommit(ui, repo, *pats, **opts):
2019 if opts.get('interactive'):
2019 if opts.get('interactive'):
2020 opts.pop('interactive')
2020 opts.pop('interactive')
2021 ret = cmdutil.dorecord(
2021 ret = cmdutil.dorecord(
2022 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2022 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2023 )
2023 )
2024 # ret can be 0 (no changes to record) or the value returned by
2024 # ret can be 0 (no changes to record) or the value returned by
2025 # commit(), 1 if nothing changed or None on success.
2025 # commit(), 1 if nothing changed or None on success.
2026 return 1 if ret == 0 else ret
2026 return 1 if ret == 0 else ret
2027
2027
2028 opts = pycompat.byteskwargs(opts)
2028 opts = pycompat.byteskwargs(opts)
2029 if opts.get(b'subrepos'):
2029 if opts.get(b'subrepos'):
2030 if opts.get(b'amend'):
2030 if opts.get(b'amend'):
2031 raise error.Abort(_(b'cannot amend with --subrepos'))
2031 raise error.Abort(_(b'cannot amend with --subrepos'))
2032 # Let --subrepos on the command line override config setting.
2032 # Let --subrepos on the command line override config setting.
2033 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2033 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2034
2034
2035 cmdutil.checkunfinished(repo, commit=True)
2035 cmdutil.checkunfinished(repo, commit=True)
2036
2036
2037 branch = repo[None].branch()
2037 branch = repo[None].branch()
2038 bheads = repo.branchheads(branch)
2038 bheads = repo.branchheads(branch)
2039
2039
2040 extra = {}
2040 extra = {}
2041 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2041 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2042 extra[b'close'] = b'1'
2042 extra[b'close'] = b'1'
2043
2043
2044 if repo[b'.'].closesbranch():
2044 if repo[b'.'].closesbranch():
2045 raise error.Abort(
2045 raise error.Abort(
2046 _(b'current revision is already a branch closing head')
2046 _(b'current revision is already a branch closing head')
2047 )
2047 )
2048 elif not bheads:
2048 elif not bheads:
2049 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2049 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2050 elif (
2050 elif (
2051 branch == repo[b'.'].branch()
2051 branch == repo[b'.'].branch()
2052 and repo[b'.'].node() not in bheads
2052 and repo[b'.'].node() not in bheads
2053 and not opts.get(b'force_close_branch')
2053 and not opts.get(b'force_close_branch')
2054 ):
2054 ):
2055 hint = _(
2055 hint = _(
2056 b'use --force-close-branch to close branch from a non-head'
2056 b'use --force-close-branch to close branch from a non-head'
2057 b' changeset'
2057 b' changeset'
2058 )
2058 )
2059 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2059 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2060 elif opts.get(b'amend'):
2060 elif opts.get(b'amend'):
2061 if (
2061 if (
2062 repo[b'.'].p1().branch() != branch
2062 repo[b'.'].p1().branch() != branch
2063 and repo[b'.'].p2().branch() != branch
2063 and repo[b'.'].p2().branch() != branch
2064 ):
2064 ):
2065 raise error.Abort(_(b'can only close branch heads'))
2065 raise error.Abort(_(b'can only close branch heads'))
2066
2066
2067 if opts.get(b'amend'):
2067 if opts.get(b'amend'):
2068 if ui.configbool(b'ui', b'commitsubrepos'):
2068 if ui.configbool(b'ui', b'commitsubrepos'):
2069 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2069 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2070
2070
2071 old = repo[b'.']
2071 old = repo[b'.']
2072 rewriteutil.precheck(repo, [old.rev()], b'amend')
2072 rewriteutil.precheck(repo, [old.rev()], b'amend')
2073
2073
2074 # Currently histedit gets confused if an amend happens while histedit
2074 # Currently histedit gets confused if an amend happens while histedit
2075 # is in progress. Since we have a checkunfinished command, we are
2075 # is in progress. Since we have a checkunfinished command, we are
2076 # temporarily honoring it.
2076 # temporarily honoring it.
2077 #
2077 #
2078 # Note: eventually this guard will be removed. Please do not expect
2078 # Note: eventually this guard will be removed. Please do not expect
2079 # this behavior to remain.
2079 # this behavior to remain.
2080 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2080 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2081 cmdutil.checkunfinished(repo)
2081 cmdutil.checkunfinished(repo)
2082
2082
2083 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2083 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2084 if node == old.node():
2084 if node == old.node():
2085 ui.status(_(b"nothing changed\n"))
2085 ui.status(_(b"nothing changed\n"))
2086 return 1
2086 return 1
2087 else:
2087 else:
2088
2088
2089 def commitfunc(ui, repo, message, match, opts):
2089 def commitfunc(ui, repo, message, match, opts):
2090 overrides = {}
2090 overrides = {}
2091 if opts.get(b'secret'):
2091 if opts.get(b'secret'):
2092 overrides[(b'phases', b'new-commit')] = b'secret'
2092 overrides[(b'phases', b'new-commit')] = b'secret'
2093
2093
2094 baseui = repo.baseui
2094 baseui = repo.baseui
2095 with baseui.configoverride(overrides, b'commit'):
2095 with baseui.configoverride(overrides, b'commit'):
2096 with ui.configoverride(overrides, b'commit'):
2096 with ui.configoverride(overrides, b'commit'):
2097 editform = cmdutil.mergeeditform(
2097 editform = cmdutil.mergeeditform(
2098 repo[None], b'commit.normal'
2098 repo[None], b'commit.normal'
2099 )
2099 )
2100 editor = cmdutil.getcommiteditor(
2100 editor = cmdutil.getcommiteditor(
2101 editform=editform, **pycompat.strkwargs(opts)
2101 editform=editform, **pycompat.strkwargs(opts)
2102 )
2102 )
2103 return repo.commit(
2103 return repo.commit(
2104 message,
2104 message,
2105 opts.get(b'user'),
2105 opts.get(b'user'),
2106 opts.get(b'date'),
2106 opts.get(b'date'),
2107 match,
2107 match,
2108 editor=editor,
2108 editor=editor,
2109 extra=extra,
2109 extra=extra,
2110 )
2110 )
2111
2111
2112 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2112 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2113
2113
2114 if not node:
2114 if not node:
2115 stat = cmdutil.postcommitstatus(repo, pats, opts)
2115 stat = cmdutil.postcommitstatus(repo, pats, opts)
2116 if stat.deleted:
2116 if stat.deleted:
2117 ui.status(
2117 ui.status(
2118 _(
2118 _(
2119 b"nothing changed (%d missing files, see "
2119 b"nothing changed (%d missing files, see "
2120 b"'hg status')\n"
2120 b"'hg status')\n"
2121 )
2121 )
2122 % len(stat.deleted)
2122 % len(stat.deleted)
2123 )
2123 )
2124 else:
2124 else:
2125 ui.status(_(b"nothing changed\n"))
2125 ui.status(_(b"nothing changed\n"))
2126 return 1
2126 return 1
2127
2127
2128 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2128 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2129
2129
2130 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2130 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2131 status(
2131 status(
2132 ui,
2132 ui,
2133 repo,
2133 repo,
2134 modified=True,
2134 modified=True,
2135 added=True,
2135 added=True,
2136 removed=True,
2136 removed=True,
2137 deleted=True,
2137 deleted=True,
2138 unknown=True,
2138 unknown=True,
2139 subrepos=opts.get(b'subrepos'),
2139 subrepos=opts.get(b'subrepos'),
2140 )
2140 )
2141
2141
2142
2142
2143 @command(
2143 @command(
2144 b'config|showconfig|debugconfig',
2144 b'config|showconfig|debugconfig',
2145 [
2145 [
2146 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2146 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2147 (b'e', b'edit', None, _(b'edit user config')),
2147 (b'e', b'edit', None, _(b'edit user config')),
2148 (b'l', b'local', None, _(b'edit repository config')),
2148 (b'l', b'local', None, _(b'edit repository config')),
2149 (b'g', b'global', None, _(b'edit global config')),
2149 (b'g', b'global', None, _(b'edit global config')),
2150 ]
2150 ]
2151 + formatteropts,
2151 + formatteropts,
2152 _(b'[-u] [NAME]...'),
2152 _(b'[-u] [NAME]...'),
2153 helpcategory=command.CATEGORY_HELP,
2153 helpcategory=command.CATEGORY_HELP,
2154 optionalrepo=True,
2154 optionalrepo=True,
2155 intents={INTENT_READONLY},
2155 intents={INTENT_READONLY},
2156 )
2156 )
2157 def config(ui, repo, *values, **opts):
2157 def config(ui, repo, *values, **opts):
2158 """show combined config settings from all hgrc files
2158 """show combined config settings from all hgrc files
2159
2159
2160 With no arguments, print names and values of all config items.
2160 With no arguments, print names and values of all config items.
2161
2161
2162 With one argument of the form section.name, print just the value
2162 With one argument of the form section.name, print just the value
2163 of that config item.
2163 of that config item.
2164
2164
2165 With multiple arguments, print names and values of all config
2165 With multiple arguments, print names and values of all config
2166 items with matching section names or section.names.
2166 items with matching section names or section.names.
2167
2167
2168 With --edit, start an editor on the user-level config file. With
2168 With --edit, start an editor on the user-level config file. With
2169 --global, edit the system-wide config file. With --local, edit the
2169 --global, edit the system-wide config file. With --local, edit the
2170 repository-level config file.
2170 repository-level config file.
2171
2171
2172 With --debug, the source (filename and line number) is printed
2172 With --debug, the source (filename and line number) is printed
2173 for each config item.
2173 for each config item.
2174
2174
2175 See :hg:`help config` for more information about config files.
2175 See :hg:`help config` for more information about config files.
2176
2176
2177 .. container:: verbose
2177 .. container:: verbose
2178
2178
2179 Template:
2179 Template:
2180
2180
2181 The following keywords are supported. See also :hg:`help templates`.
2181 The following keywords are supported. See also :hg:`help templates`.
2182
2182
2183 :name: String. Config name.
2183 :name: String. Config name.
2184 :source: String. Filename and line number where the item is defined.
2184 :source: String. Filename and line number where the item is defined.
2185 :value: String. Config value.
2185 :value: String. Config value.
2186
2186
2187 Returns 0 on success, 1 if NAME does not exist.
2187 Returns 0 on success, 1 if NAME does not exist.
2188
2188
2189 """
2189 """
2190
2190
2191 opts = pycompat.byteskwargs(opts)
2191 opts = pycompat.byteskwargs(opts)
2192 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2192 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2193 if opts.get(b'local') and opts.get(b'global'):
2193 if opts.get(b'local') and opts.get(b'global'):
2194 raise error.Abort(_(b"can't use --local and --global together"))
2194 raise error.Abort(_(b"can't use --local and --global together"))
2195
2195
2196 if opts.get(b'local'):
2196 if opts.get(b'local'):
2197 if not repo:
2197 if not repo:
2198 raise error.Abort(_(b"can't use --local outside a repository"))
2198 raise error.Abort(_(b"can't use --local outside a repository"))
2199 paths = [repo.vfs.join(b'hgrc')]
2199 paths = [repo.vfs.join(b'hgrc')]
2200 elif opts.get(b'global'):
2200 elif opts.get(b'global'):
2201 paths = rcutil.systemrcpath()
2201 paths = rcutil.systemrcpath()
2202 else:
2202 else:
2203 paths = rcutil.userrcpath()
2203 paths = rcutil.userrcpath()
2204
2204
2205 for f in paths:
2205 for f in paths:
2206 if os.path.exists(f):
2206 if os.path.exists(f):
2207 break
2207 break
2208 else:
2208 else:
2209 if opts.get(b'global'):
2209 if opts.get(b'global'):
2210 samplehgrc = uimod.samplehgrcs[b'global']
2210 samplehgrc = uimod.samplehgrcs[b'global']
2211 elif opts.get(b'local'):
2211 elif opts.get(b'local'):
2212 samplehgrc = uimod.samplehgrcs[b'local']
2212 samplehgrc = uimod.samplehgrcs[b'local']
2213 else:
2213 else:
2214 samplehgrc = uimod.samplehgrcs[b'user']
2214 samplehgrc = uimod.samplehgrcs[b'user']
2215
2215
2216 f = paths[0]
2216 f = paths[0]
2217 fp = open(f, b"wb")
2217 fp = open(f, b"wb")
2218 fp.write(util.tonativeeol(samplehgrc))
2218 fp.write(util.tonativeeol(samplehgrc))
2219 fp.close()
2219 fp.close()
2220
2220
2221 editor = ui.geteditor()
2221 editor = ui.geteditor()
2222 ui.system(
2222 ui.system(
2223 b"%s \"%s\"" % (editor, f),
2223 b"%s \"%s\"" % (editor, f),
2224 onerr=error.Abort,
2224 onerr=error.Abort,
2225 errprefix=_(b"edit failed"),
2225 errprefix=_(b"edit failed"),
2226 blockedtag=b'config_edit',
2226 blockedtag=b'config_edit',
2227 )
2227 )
2228 return
2228 return
2229 ui.pager(b'config')
2229 ui.pager(b'config')
2230 fm = ui.formatter(b'config', opts)
2230 fm = ui.formatter(b'config', opts)
2231 for t, f in rcutil.rccomponents():
2231 for t, f in rcutil.rccomponents():
2232 if t == b'path':
2232 if t == b'path':
2233 ui.debug(b'read config from: %s\n' % f)
2233 ui.debug(b'read config from: %s\n' % f)
2234 elif t == b'items':
2234 elif t == b'items':
2235 for section, name, value, source in f:
2235 for section, name, value, source in f:
2236 ui.debug(b'set config by: %s\n' % source)
2236 ui.debug(b'set config by: %s\n' % source)
2237 else:
2237 else:
2238 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2238 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2239 untrusted = bool(opts.get(b'untrusted'))
2239 untrusted = bool(opts.get(b'untrusted'))
2240
2240
2241 selsections = selentries = []
2241 selsections = selentries = []
2242 if values:
2242 if values:
2243 selsections = [v for v in values if b'.' not in v]
2243 selsections = [v for v in values if b'.' not in v]
2244 selentries = [v for v in values if b'.' in v]
2244 selentries = [v for v in values if b'.' in v]
2245 uniquesel = len(selentries) == 1 and not selsections
2245 uniquesel = len(selentries) == 1 and not selsections
2246 selsections = set(selsections)
2246 selsections = set(selsections)
2247 selentries = set(selentries)
2247 selentries = set(selentries)
2248
2248
2249 matched = False
2249 matched = False
2250 for section, name, value in ui.walkconfig(untrusted=untrusted):
2250 for section, name, value in ui.walkconfig(untrusted=untrusted):
2251 source = ui.configsource(section, name, untrusted)
2251 source = ui.configsource(section, name, untrusted)
2252 value = pycompat.bytestr(value)
2252 value = pycompat.bytestr(value)
2253 defaultvalue = ui.configdefault(section, name)
2253 defaultvalue = ui.configdefault(section, name)
2254 if fm.isplain():
2254 if fm.isplain():
2255 source = source or b'none'
2255 source = source or b'none'
2256 value = value.replace(b'\n', b'\\n')
2256 value = value.replace(b'\n', b'\\n')
2257 entryname = section + b'.' + name
2257 entryname = section + b'.' + name
2258 if values and not (section in selsections or entryname in selentries):
2258 if values and not (section in selsections or entryname in selentries):
2259 continue
2259 continue
2260 fm.startitem()
2260 fm.startitem()
2261 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2261 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2262 if uniquesel:
2262 if uniquesel:
2263 fm.data(name=entryname)
2263 fm.data(name=entryname)
2264 fm.write(b'value', b'%s\n', value)
2264 fm.write(b'value', b'%s\n', value)
2265 else:
2265 else:
2266 fm.write(b'name value', b'%s=%s\n', entryname, value)
2266 fm.write(b'name value', b'%s=%s\n', entryname, value)
2267 if formatter.isprintable(defaultvalue):
2267 if formatter.isprintable(defaultvalue):
2268 fm.data(defaultvalue=defaultvalue)
2268 fm.data(defaultvalue=defaultvalue)
2269 elif isinstance(defaultvalue, list) and all(
2269 elif isinstance(defaultvalue, list) and all(
2270 formatter.isprintable(e) for e in defaultvalue
2270 formatter.isprintable(e) for e in defaultvalue
2271 ):
2271 ):
2272 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2272 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2273 # TODO: no idea how to process unsupported defaultvalue types
2273 # TODO: no idea how to process unsupported defaultvalue types
2274 matched = True
2274 matched = True
2275 fm.end()
2275 fm.end()
2276 if matched:
2276 if matched:
2277 return 0
2277 return 0
2278 return 1
2278 return 1
2279
2279
2280
2280
2281 @command(
2281 @command(
2282 b'continue',
2282 b'continue',
2283 dryrunopts,
2283 dryrunopts,
2284 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2284 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2285 helpbasic=True,
2285 helpbasic=True,
2286 )
2286 )
2287 def continuecmd(ui, repo, **opts):
2287 def continuecmd(ui, repo, **opts):
2288 """resumes an interrupted operation (EXPERIMENTAL)
2288 """resumes an interrupted operation (EXPERIMENTAL)
2289
2289
2290 Finishes a multistep operation like graft, histedit, rebase, merge,
2290 Finishes a multistep operation like graft, histedit, rebase, merge,
2291 and unshelve if they are in an interrupted state.
2291 and unshelve if they are in an interrupted state.
2292
2292
2293 use --dry-run/-n to dry run the command.
2293 use --dry-run/-n to dry run the command.
2294 """
2294 """
2295 dryrun = opts.get('dry_run')
2295 dryrun = opts.get('dry_run')
2296 contstate = cmdutil.getunfinishedstate(repo)
2296 contstate = cmdutil.getunfinishedstate(repo)
2297 if not contstate:
2297 if not contstate:
2298 raise error.Abort(_(b'no operation in progress'))
2298 raise error.Abort(_(b'no operation in progress'))
2299 if not contstate.continuefunc:
2299 if not contstate.continuefunc:
2300 raise error.Abort(
2300 raise error.Abort(
2301 (
2301 (
2302 _(b"%s in progress but does not support 'hg continue'")
2302 _(b"%s in progress but does not support 'hg continue'")
2303 % (contstate._opname)
2303 % (contstate._opname)
2304 ),
2304 ),
2305 hint=contstate.continuemsg(),
2305 hint=contstate.continuemsg(),
2306 )
2306 )
2307 if dryrun:
2307 if dryrun:
2308 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2308 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2309 return
2309 return
2310 return contstate.continuefunc(ui, repo)
2310 return contstate.continuefunc(ui, repo)
2311
2311
2312
2312
2313 @command(
2313 @command(
2314 b'copy|cp',
2314 b'copy|cp',
2315 [
2315 [
2316 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2316 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2317 (
2317 (
2318 b'f',
2318 b'f',
2319 b'force',
2319 b'force',
2320 None,
2320 None,
2321 _(b'forcibly copy over an existing managed file'),
2321 _(b'forcibly copy over an existing managed file'),
2322 ),
2322 ),
2323 ]
2323 ]
2324 + walkopts
2324 + walkopts
2325 + dryrunopts,
2325 + dryrunopts,
2326 _(b'[OPTION]... SOURCE... DEST'),
2326 _(b'[OPTION]... SOURCE... DEST'),
2327 helpcategory=command.CATEGORY_FILE_CONTENTS,
2327 helpcategory=command.CATEGORY_FILE_CONTENTS,
2328 )
2328 )
2329 def copy(ui, repo, *pats, **opts):
2329 def copy(ui, repo, *pats, **opts):
2330 """mark files as copied for the next commit
2330 """mark files as copied for the next commit
2331
2331
2332 Mark dest as having copies of source files. If dest is a
2332 Mark dest as having copies of source files. If dest is a
2333 directory, copies are put in that directory. If dest is a file,
2333 directory, copies are put in that directory. If dest is a file,
2334 the source must be a single file.
2334 the source must be a single file.
2335
2335
2336 By default, this command copies the contents of files as they
2336 By default, this command copies the contents of files as they
2337 exist in the working directory. If invoked with -A/--after, the
2337 exist in the working directory. If invoked with -A/--after, the
2338 operation is recorded, but no copying is performed.
2338 operation is recorded, but no copying is performed.
2339
2339
2340 This command takes effect with the next commit. To undo a copy
2340 This command takes effect with the next commit. To undo a copy
2341 before that, see :hg:`revert`.
2341 before that, see :hg:`revert`.
2342
2342
2343 Returns 0 on success, 1 if errors are encountered.
2343 Returns 0 on success, 1 if errors are encountered.
2344 """
2344 """
2345 opts = pycompat.byteskwargs(opts)
2345 opts = pycompat.byteskwargs(opts)
2346 with repo.wlock(False):
2346 with repo.wlock(False):
2347 return cmdutil.copy(ui, repo, pats, opts)
2347 return cmdutil.copy(ui, repo, pats, opts)
2348
2348
2349
2349
2350 @command(
2350 @command(
2351 b'debugcommands',
2351 b'debugcommands',
2352 [],
2352 [],
2353 _(b'[COMMAND]'),
2353 _(b'[COMMAND]'),
2354 helpcategory=command.CATEGORY_HELP,
2354 helpcategory=command.CATEGORY_HELP,
2355 norepo=True,
2355 norepo=True,
2356 )
2356 )
2357 def debugcommands(ui, cmd=b'', *args):
2357 def debugcommands(ui, cmd=b'', *args):
2358 """list all available commands and options"""
2358 """list all available commands and options"""
2359 for cmd, vals in sorted(pycompat.iteritems(table)):
2359 for cmd, vals in sorted(pycompat.iteritems(table)):
2360 cmd = cmd.split(b'|')[0]
2360 cmd = cmd.split(b'|')[0]
2361 opts = b', '.join([i[1] for i in vals[1]])
2361 opts = b', '.join([i[1] for i in vals[1]])
2362 ui.write(b'%s: %s\n' % (cmd, opts))
2362 ui.write(b'%s: %s\n' % (cmd, opts))
2363
2363
2364
2364
2365 @command(
2365 @command(
2366 b'debugcomplete',
2366 b'debugcomplete',
2367 [(b'o', b'options', None, _(b'show the command options'))],
2367 [(b'o', b'options', None, _(b'show the command options'))],
2368 _(b'[-o] CMD'),
2368 _(b'[-o] CMD'),
2369 helpcategory=command.CATEGORY_HELP,
2369 helpcategory=command.CATEGORY_HELP,
2370 norepo=True,
2370 norepo=True,
2371 )
2371 )
2372 def debugcomplete(ui, cmd=b'', **opts):
2372 def debugcomplete(ui, cmd=b'', **opts):
2373 """returns the completion list associated with the given command"""
2373 """returns the completion list associated with the given command"""
2374
2374
2375 if opts.get('options'):
2375 if opts.get('options'):
2376 options = []
2376 options = []
2377 otables = [globalopts]
2377 otables = [globalopts]
2378 if cmd:
2378 if cmd:
2379 aliases, entry = cmdutil.findcmd(cmd, table, False)
2379 aliases, entry = cmdutil.findcmd(cmd, table, False)
2380 otables.append(entry[1])
2380 otables.append(entry[1])
2381 for t in otables:
2381 for t in otables:
2382 for o in t:
2382 for o in t:
2383 if b"(DEPRECATED)" in o[3]:
2383 if b"(DEPRECATED)" in o[3]:
2384 continue
2384 continue
2385 if o[0]:
2385 if o[0]:
2386 options.append(b'-%s' % o[0])
2386 options.append(b'-%s' % o[0])
2387 options.append(b'--%s' % o[1])
2387 options.append(b'--%s' % o[1])
2388 ui.write(b"%s\n" % b"\n".join(options))
2388 ui.write(b"%s\n" % b"\n".join(options))
2389 return
2389 return
2390
2390
2391 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2391 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2392 if ui.verbose:
2392 if ui.verbose:
2393 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2393 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2394 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2394 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2395
2395
2396
2396
2397 @command(
2397 @command(
2398 b'diff',
2398 b'diff',
2399 [
2399 [
2400 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2400 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2401 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2401 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2402 ]
2402 ]
2403 + diffopts
2403 + diffopts
2404 + diffopts2
2404 + diffopts2
2405 + walkopts
2405 + walkopts
2406 + subrepoopts,
2406 + subrepoopts,
2407 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2407 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2408 helpcategory=command.CATEGORY_FILE_CONTENTS,
2408 helpcategory=command.CATEGORY_FILE_CONTENTS,
2409 helpbasic=True,
2409 helpbasic=True,
2410 inferrepo=True,
2410 inferrepo=True,
2411 intents={INTENT_READONLY},
2411 intents={INTENT_READONLY},
2412 )
2412 )
2413 def diff(ui, repo, *pats, **opts):
2413 def diff(ui, repo, *pats, **opts):
2414 """diff repository (or selected files)
2414 """diff repository (or selected files)
2415
2415
2416 Show differences between revisions for the specified files.
2416 Show differences between revisions for the specified files.
2417
2417
2418 Differences between files are shown using the unified diff format.
2418 Differences between files are shown using the unified diff format.
2419
2419
2420 .. note::
2420 .. note::
2421
2421
2422 :hg:`diff` may generate unexpected results for merges, as it will
2422 :hg:`diff` may generate unexpected results for merges, as it will
2423 default to comparing against the working directory's first
2423 default to comparing against the working directory's first
2424 parent changeset if no revisions are specified.
2424 parent changeset if no revisions are specified.
2425
2425
2426 When two revision arguments are given, then changes are shown
2426 When two revision arguments are given, then changes are shown
2427 between those revisions. If only one revision is specified then
2427 between those revisions. If only one revision is specified then
2428 that revision is compared to the working directory, and, when no
2428 that revision is compared to the working directory, and, when no
2429 revisions are specified, the working directory files are compared
2429 revisions are specified, the working directory files are compared
2430 to its first parent.
2430 to its first parent.
2431
2431
2432 Alternatively you can specify -c/--change with a revision to see
2432 Alternatively you can specify -c/--change with a revision to see
2433 the changes in that changeset relative to its first parent.
2433 the changes in that changeset relative to its first parent.
2434
2434
2435 Without the -a/--text option, diff will avoid generating diffs of
2435 Without the -a/--text option, diff will avoid generating diffs of
2436 files it detects as binary. With -a, diff will generate a diff
2436 files it detects as binary. With -a, diff will generate a diff
2437 anyway, probably with undesirable results.
2437 anyway, probably with undesirable results.
2438
2438
2439 Use the -g/--git option to generate diffs in the git extended diff
2439 Use the -g/--git option to generate diffs in the git extended diff
2440 format. For more information, read :hg:`help diffs`.
2440 format. For more information, read :hg:`help diffs`.
2441
2441
2442 .. container:: verbose
2442 .. container:: verbose
2443
2443
2444 Examples:
2444 Examples:
2445
2445
2446 - compare a file in the current working directory to its parent::
2446 - compare a file in the current working directory to its parent::
2447
2447
2448 hg diff foo.c
2448 hg diff foo.c
2449
2449
2450 - compare two historical versions of a directory, with rename info::
2450 - compare two historical versions of a directory, with rename info::
2451
2451
2452 hg diff --git -r 1.0:1.2 lib/
2452 hg diff --git -r 1.0:1.2 lib/
2453
2453
2454 - get change stats relative to the last change on some date::
2454 - get change stats relative to the last change on some date::
2455
2455
2456 hg diff --stat -r "date('may 2')"
2456 hg diff --stat -r "date('may 2')"
2457
2457
2458 - diff all newly-added files that contain a keyword::
2458 - diff all newly-added files that contain a keyword::
2459
2459
2460 hg diff "set:added() and grep(GNU)"
2460 hg diff "set:added() and grep(GNU)"
2461
2461
2462 - compare a revision and its parents::
2462 - compare a revision and its parents::
2463
2463
2464 hg diff -c 9353 # compare against first parent
2464 hg diff -c 9353 # compare against first parent
2465 hg diff -r 9353^:9353 # same using revset syntax
2465 hg diff -r 9353^:9353 # same using revset syntax
2466 hg diff -r 9353^2:9353 # compare against the second parent
2466 hg diff -r 9353^2:9353 # compare against the second parent
2467
2467
2468 Returns 0 on success.
2468 Returns 0 on success.
2469 """
2469 """
2470
2470
2471 opts = pycompat.byteskwargs(opts)
2471 opts = pycompat.byteskwargs(opts)
2472 revs = opts.get(b'rev')
2472 revs = opts.get(b'rev')
2473 change = opts.get(b'change')
2473 change = opts.get(b'change')
2474 stat = opts.get(b'stat')
2474 stat = opts.get(b'stat')
2475 reverse = opts.get(b'reverse')
2475 reverse = opts.get(b'reverse')
2476
2476
2477 if revs and change:
2477 if revs and change:
2478 msg = _(b'cannot specify --rev and --change at the same time')
2478 msg = _(b'cannot specify --rev and --change at the same time')
2479 raise error.Abort(msg)
2479 raise error.Abort(msg)
2480 elif change:
2480 elif change:
2481 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2481 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2482 ctx2 = scmutil.revsingle(repo, change, None)
2482 ctx2 = scmutil.revsingle(repo, change, None)
2483 ctx1 = ctx2.p1()
2483 ctx1 = ctx2.p1()
2484 else:
2484 else:
2485 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2485 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2486 ctx1, ctx2 = scmutil.revpair(repo, revs)
2486 ctx1, ctx2 = scmutil.revpair(repo, revs)
2487 node1, node2 = ctx1.node(), ctx2.node()
2487 node1, node2 = ctx1.node(), ctx2.node()
2488
2488
2489 if reverse:
2489 if reverse:
2490 node1, node2 = node2, node1
2490 node1, node2 = node2, node1
2491
2491
2492 diffopts = patch.diffallopts(ui, opts)
2492 diffopts = patch.diffallopts(ui, opts)
2493 m = scmutil.match(ctx2, pats, opts)
2493 m = scmutil.match(ctx2, pats, opts)
2494 m = repo.narrowmatch(m)
2494 m = repo.narrowmatch(m)
2495 ui.pager(b'diff')
2495 ui.pager(b'diff')
2496 logcmdutil.diffordiffstat(
2496 logcmdutil.diffordiffstat(
2497 ui,
2497 ui,
2498 repo,
2498 repo,
2499 diffopts,
2499 diffopts,
2500 node1,
2500 node1,
2501 node2,
2501 node2,
2502 m,
2502 m,
2503 stat=stat,
2503 stat=stat,
2504 listsubrepos=opts.get(b'subrepos'),
2504 listsubrepos=opts.get(b'subrepos'),
2505 root=opts.get(b'root'),
2505 root=opts.get(b'root'),
2506 )
2506 )
2507
2507
2508
2508
2509 @command(
2509 @command(
2510 b'export',
2510 b'export',
2511 [
2511 [
2512 (
2512 (
2513 b'B',
2513 b'B',
2514 b'bookmark',
2514 b'bookmark',
2515 b'',
2515 b'',
2516 _(b'export changes only reachable by given bookmark'),
2516 _(b'export changes only reachable by given bookmark'),
2517 _(b'BOOKMARK'),
2517 _(b'BOOKMARK'),
2518 ),
2518 ),
2519 (
2519 (
2520 b'o',
2520 b'o',
2521 b'output',
2521 b'output',
2522 b'',
2522 b'',
2523 _(b'print output to file with formatted name'),
2523 _(b'print output to file with formatted name'),
2524 _(b'FORMAT'),
2524 _(b'FORMAT'),
2525 ),
2525 ),
2526 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2526 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2527 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2527 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2528 ]
2528 ]
2529 + diffopts
2529 + diffopts
2530 + formatteropts,
2530 + formatteropts,
2531 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2531 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2532 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2532 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2533 helpbasic=True,
2533 helpbasic=True,
2534 intents={INTENT_READONLY},
2534 intents={INTENT_READONLY},
2535 )
2535 )
2536 def export(ui, repo, *changesets, **opts):
2536 def export(ui, repo, *changesets, **opts):
2537 """dump the header and diffs for one or more changesets
2537 """dump the header and diffs for one or more changesets
2538
2538
2539 Print the changeset header and diffs for one or more revisions.
2539 Print the changeset header and diffs for one or more revisions.
2540 If no revision is given, the parent of the working directory is used.
2540 If no revision is given, the parent of the working directory is used.
2541
2541
2542 The information shown in the changeset header is: author, date,
2542 The information shown in the changeset header is: author, date,
2543 branch name (if non-default), changeset hash, parent(s) and commit
2543 branch name (if non-default), changeset hash, parent(s) and commit
2544 comment.
2544 comment.
2545
2545
2546 .. note::
2546 .. note::
2547
2547
2548 :hg:`export` may generate unexpected diff output for merge
2548 :hg:`export` may generate unexpected diff output for merge
2549 changesets, as it will compare the merge changeset against its
2549 changesets, as it will compare the merge changeset against its
2550 first parent only.
2550 first parent only.
2551
2551
2552 Output may be to a file, in which case the name of the file is
2552 Output may be to a file, in which case the name of the file is
2553 given using a template string. See :hg:`help templates`. In addition
2553 given using a template string. See :hg:`help templates`. In addition
2554 to the common template keywords, the following formatting rules are
2554 to the common template keywords, the following formatting rules are
2555 supported:
2555 supported:
2556
2556
2557 :``%%``: literal "%" character
2557 :``%%``: literal "%" character
2558 :``%H``: changeset hash (40 hexadecimal digits)
2558 :``%H``: changeset hash (40 hexadecimal digits)
2559 :``%N``: number of patches being generated
2559 :``%N``: number of patches being generated
2560 :``%R``: changeset revision number
2560 :``%R``: changeset revision number
2561 :``%b``: basename of the exporting repository
2561 :``%b``: basename of the exporting repository
2562 :``%h``: short-form changeset hash (12 hexadecimal digits)
2562 :``%h``: short-form changeset hash (12 hexadecimal digits)
2563 :``%m``: first line of the commit message (only alphanumeric characters)
2563 :``%m``: first line of the commit message (only alphanumeric characters)
2564 :``%n``: zero-padded sequence number, starting at 1
2564 :``%n``: zero-padded sequence number, starting at 1
2565 :``%r``: zero-padded changeset revision number
2565 :``%r``: zero-padded changeset revision number
2566 :``\\``: literal "\\" character
2566 :``\\``: literal "\\" character
2567
2567
2568 Without the -a/--text option, export will avoid generating diffs
2568 Without the -a/--text option, export will avoid generating diffs
2569 of files it detects as binary. With -a, export will generate a
2569 of files it detects as binary. With -a, export will generate a
2570 diff anyway, probably with undesirable results.
2570 diff anyway, probably with undesirable results.
2571
2571
2572 With -B/--bookmark changesets reachable by the given bookmark are
2572 With -B/--bookmark changesets reachable by the given bookmark are
2573 selected.
2573 selected.
2574
2574
2575 Use the -g/--git option to generate diffs in the git extended diff
2575 Use the -g/--git option to generate diffs in the git extended diff
2576 format. See :hg:`help diffs` for more information.
2576 format. See :hg:`help diffs` for more information.
2577
2577
2578 With the --switch-parent option, the diff will be against the
2578 With the --switch-parent option, the diff will be against the
2579 second parent. It can be useful to review a merge.
2579 second parent. It can be useful to review a merge.
2580
2580
2581 .. container:: verbose
2581 .. container:: verbose
2582
2582
2583 Template:
2583 Template:
2584
2584
2585 The following keywords are supported in addition to the common template
2585 The following keywords are supported in addition to the common template
2586 keywords and functions. See also :hg:`help templates`.
2586 keywords and functions. See also :hg:`help templates`.
2587
2587
2588 :diff: String. Diff content.
2588 :diff: String. Diff content.
2589 :parents: List of strings. Parent nodes of the changeset.
2589 :parents: List of strings. Parent nodes of the changeset.
2590
2590
2591 Examples:
2591 Examples:
2592
2592
2593 - use export and import to transplant a bugfix to the current
2593 - use export and import to transplant a bugfix to the current
2594 branch::
2594 branch::
2595
2595
2596 hg export -r 9353 | hg import -
2596 hg export -r 9353 | hg import -
2597
2597
2598 - export all the changesets between two revisions to a file with
2598 - export all the changesets between two revisions to a file with
2599 rename information::
2599 rename information::
2600
2600
2601 hg export --git -r 123:150 > changes.txt
2601 hg export --git -r 123:150 > changes.txt
2602
2602
2603 - split outgoing changes into a series of patches with
2603 - split outgoing changes into a series of patches with
2604 descriptive names::
2604 descriptive names::
2605
2605
2606 hg export -r "outgoing()" -o "%n-%m.patch"
2606 hg export -r "outgoing()" -o "%n-%m.patch"
2607
2607
2608 Returns 0 on success.
2608 Returns 0 on success.
2609 """
2609 """
2610 opts = pycompat.byteskwargs(opts)
2610 opts = pycompat.byteskwargs(opts)
2611 bookmark = opts.get(b'bookmark')
2611 bookmark = opts.get(b'bookmark')
2612 changesets += tuple(opts.get(b'rev', []))
2612 changesets += tuple(opts.get(b'rev', []))
2613
2613
2614 if bookmark and changesets:
2614 if bookmark and changesets:
2615 raise error.Abort(_(b"-r and -B are mutually exclusive"))
2615 raise error.Abort(_(b"-r and -B are mutually exclusive"))
2616
2616
2617 if bookmark:
2617 if bookmark:
2618 if bookmark not in repo._bookmarks:
2618 if bookmark not in repo._bookmarks:
2619 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2619 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2620
2620
2621 revs = scmutil.bookmarkrevs(repo, bookmark)
2621 revs = scmutil.bookmarkrevs(repo, bookmark)
2622 else:
2622 else:
2623 if not changesets:
2623 if not changesets:
2624 changesets = [b'.']
2624 changesets = [b'.']
2625
2625
2626 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2626 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2627 revs = scmutil.revrange(repo, changesets)
2627 revs = scmutil.revrange(repo, changesets)
2628
2628
2629 if not revs:
2629 if not revs:
2630 raise error.Abort(_(b"export requires at least one changeset"))
2630 raise error.Abort(_(b"export requires at least one changeset"))
2631 if len(revs) > 1:
2631 if len(revs) > 1:
2632 ui.note(_(b'exporting patches:\n'))
2632 ui.note(_(b'exporting patches:\n'))
2633 else:
2633 else:
2634 ui.note(_(b'exporting patch:\n'))
2634 ui.note(_(b'exporting patch:\n'))
2635
2635
2636 fntemplate = opts.get(b'output')
2636 fntemplate = opts.get(b'output')
2637 if cmdutil.isstdiofilename(fntemplate):
2637 if cmdutil.isstdiofilename(fntemplate):
2638 fntemplate = b''
2638 fntemplate = b''
2639
2639
2640 if fntemplate:
2640 if fntemplate:
2641 fm = formatter.nullformatter(ui, b'export', opts)
2641 fm = formatter.nullformatter(ui, b'export', opts)
2642 else:
2642 else:
2643 ui.pager(b'export')
2643 ui.pager(b'export')
2644 fm = ui.formatter(b'export', opts)
2644 fm = ui.formatter(b'export', opts)
2645 with fm:
2645 with fm:
2646 cmdutil.export(
2646 cmdutil.export(
2647 repo,
2647 repo,
2648 revs,
2648 revs,
2649 fm,
2649 fm,
2650 fntemplate=fntemplate,
2650 fntemplate=fntemplate,
2651 switch_parent=opts.get(b'switch_parent'),
2651 switch_parent=opts.get(b'switch_parent'),
2652 opts=patch.diffallopts(ui, opts),
2652 opts=patch.diffallopts(ui, opts),
2653 )
2653 )
2654
2654
2655
2655
2656 @command(
2656 @command(
2657 b'files',
2657 b'files',
2658 [
2658 [
2659 (
2659 (
2660 b'r',
2660 b'r',
2661 b'rev',
2661 b'rev',
2662 b'',
2662 b'',
2663 _(b'search the repository as it is in REV'),
2663 _(b'search the repository as it is in REV'),
2664 _(b'REV'),
2664 _(b'REV'),
2665 ),
2665 ),
2666 (
2666 (
2667 b'0',
2667 b'0',
2668 b'print0',
2668 b'print0',
2669 None,
2669 None,
2670 _(b'end filenames with NUL, for use with xargs'),
2670 _(b'end filenames with NUL, for use with xargs'),
2671 ),
2671 ),
2672 ]
2672 ]
2673 + walkopts
2673 + walkopts
2674 + formatteropts
2674 + formatteropts
2675 + subrepoopts,
2675 + subrepoopts,
2676 _(b'[OPTION]... [FILE]...'),
2676 _(b'[OPTION]... [FILE]...'),
2677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2678 intents={INTENT_READONLY},
2678 intents={INTENT_READONLY},
2679 )
2679 )
2680 def files(ui, repo, *pats, **opts):
2680 def files(ui, repo, *pats, **opts):
2681 """list tracked files
2681 """list tracked files
2682
2682
2683 Print files under Mercurial control in the working directory or
2683 Print files under Mercurial control in the working directory or
2684 specified revision for given files (excluding removed files).
2684 specified revision for given files (excluding removed files).
2685 Files can be specified as filenames or filesets.
2685 Files can be specified as filenames or filesets.
2686
2686
2687 If no files are given to match, this command prints the names
2687 If no files are given to match, this command prints the names
2688 of all files under Mercurial control.
2688 of all files under Mercurial control.
2689
2689
2690 .. container:: verbose
2690 .. container:: verbose
2691
2691
2692 Template:
2692 Template:
2693
2693
2694 The following keywords are supported in addition to the common template
2694 The following keywords are supported in addition to the common template
2695 keywords and functions. See also :hg:`help templates`.
2695 keywords and functions. See also :hg:`help templates`.
2696
2696
2697 :flags: String. Character denoting file's symlink and executable bits.
2697 :flags: String. Character denoting file's symlink and executable bits.
2698 :path: String. Repository-absolute path of the file.
2698 :path: String. Repository-absolute path of the file.
2699 :size: Integer. Size of the file in bytes.
2699 :size: Integer. Size of the file in bytes.
2700
2700
2701 Examples:
2701 Examples:
2702
2702
2703 - list all files under the current directory::
2703 - list all files under the current directory::
2704
2704
2705 hg files .
2705 hg files .
2706
2706
2707 - shows sizes and flags for current revision::
2707 - shows sizes and flags for current revision::
2708
2708
2709 hg files -vr .
2709 hg files -vr .
2710
2710
2711 - list all files named README::
2711 - list all files named README::
2712
2712
2713 hg files -I "**/README"
2713 hg files -I "**/README"
2714
2714
2715 - list all binary files::
2715 - list all binary files::
2716
2716
2717 hg files "set:binary()"
2717 hg files "set:binary()"
2718
2718
2719 - find files containing a regular expression::
2719 - find files containing a regular expression::
2720
2720
2721 hg files "set:grep('bob')"
2721 hg files "set:grep('bob')"
2722
2722
2723 - search tracked file contents with xargs and grep::
2723 - search tracked file contents with xargs and grep::
2724
2724
2725 hg files -0 | xargs -0 grep foo
2725 hg files -0 | xargs -0 grep foo
2726
2726
2727 See :hg:`help patterns` and :hg:`help filesets` for more information
2727 See :hg:`help patterns` and :hg:`help filesets` for more information
2728 on specifying file patterns.
2728 on specifying file patterns.
2729
2729
2730 Returns 0 if a match is found, 1 otherwise.
2730 Returns 0 if a match is found, 1 otherwise.
2731
2731
2732 """
2732 """
2733
2733
2734 opts = pycompat.byteskwargs(opts)
2734 opts = pycompat.byteskwargs(opts)
2735 rev = opts.get(b'rev')
2735 rev = opts.get(b'rev')
2736 if rev:
2736 if rev:
2737 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2737 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2738 ctx = scmutil.revsingle(repo, rev, None)
2738 ctx = scmutil.revsingle(repo, rev, None)
2739
2739
2740 end = b'\n'
2740 end = b'\n'
2741 if opts.get(b'print0'):
2741 if opts.get(b'print0'):
2742 end = b'\0'
2742 end = b'\0'
2743 fmt = b'%s' + end
2743 fmt = b'%s' + end
2744
2744
2745 m = scmutil.match(ctx, pats, opts)
2745 m = scmutil.match(ctx, pats, opts)
2746 ui.pager(b'files')
2746 ui.pager(b'files')
2747 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2747 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2748 with ui.formatter(b'files', opts) as fm:
2748 with ui.formatter(b'files', opts) as fm:
2749 return cmdutil.files(
2749 return cmdutil.files(
2750 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2750 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2751 )
2751 )
2752
2752
2753
2753
2754 @command(
2754 @command(
2755 b'forget',
2755 b'forget',
2756 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2756 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2757 + walkopts
2757 + walkopts
2758 + dryrunopts,
2758 + dryrunopts,
2759 _(b'[OPTION]... FILE...'),
2759 _(b'[OPTION]... FILE...'),
2760 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2760 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2761 helpbasic=True,
2761 helpbasic=True,
2762 inferrepo=True,
2762 inferrepo=True,
2763 )
2763 )
2764 def forget(ui, repo, *pats, **opts):
2764 def forget(ui, repo, *pats, **opts):
2765 """forget the specified files on the next commit
2765 """forget the specified files on the next commit
2766
2766
2767 Mark the specified files so they will no longer be tracked
2767 Mark the specified files so they will no longer be tracked
2768 after the next commit.
2768 after the next commit.
2769
2769
2770 This only removes files from the current branch, not from the
2770 This only removes files from the current branch, not from the
2771 entire project history, and it does not delete them from the
2771 entire project history, and it does not delete them from the
2772 working directory.
2772 working directory.
2773
2773
2774 To delete the file from the working directory, see :hg:`remove`.
2774 To delete the file from the working directory, see :hg:`remove`.
2775
2775
2776 To undo a forget before the next commit, see :hg:`add`.
2776 To undo a forget before the next commit, see :hg:`add`.
2777
2777
2778 .. container:: verbose
2778 .. container:: verbose
2779
2779
2780 Examples:
2780 Examples:
2781
2781
2782 - forget newly-added binary files::
2782 - forget newly-added binary files::
2783
2783
2784 hg forget "set:added() and binary()"
2784 hg forget "set:added() and binary()"
2785
2785
2786 - forget files that would be excluded by .hgignore::
2786 - forget files that would be excluded by .hgignore::
2787
2787
2788 hg forget "set:hgignore()"
2788 hg forget "set:hgignore()"
2789
2789
2790 Returns 0 on success.
2790 Returns 0 on success.
2791 """
2791 """
2792
2792
2793 opts = pycompat.byteskwargs(opts)
2793 opts = pycompat.byteskwargs(opts)
2794 if not pats:
2794 if not pats:
2795 raise error.Abort(_(b'no files specified'))
2795 raise error.Abort(_(b'no files specified'))
2796
2796
2797 m = scmutil.match(repo[None], pats, opts)
2797 m = scmutil.match(repo[None], pats, opts)
2798 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2798 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2799 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2799 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2800 rejected = cmdutil.forget(
2800 rejected = cmdutil.forget(
2801 ui,
2801 ui,
2802 repo,
2802 repo,
2803 m,
2803 m,
2804 prefix=b"",
2804 prefix=b"",
2805 uipathfn=uipathfn,
2805 uipathfn=uipathfn,
2806 explicitonly=False,
2806 explicitonly=False,
2807 dryrun=dryrun,
2807 dryrun=dryrun,
2808 interactive=interactive,
2808 interactive=interactive,
2809 )[0]
2809 )[0]
2810 return rejected and 1 or 0
2810 return rejected and 1 or 0
2811
2811
2812
2812
2813 @command(
2813 @command(
2814 b'graft',
2814 b'graft',
2815 [
2815 [
2816 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2816 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2817 (
2817 (
2818 b'',
2818 b'',
2819 b'base',
2819 b'base',
2820 b'',
2820 b'',
2821 _(b'base revision when doing the graft merge (ADVANCED)'),
2821 _(b'base revision when doing the graft merge (ADVANCED)'),
2822 _(b'REV'),
2822 _(b'REV'),
2823 ),
2823 ),
2824 (b'c', b'continue', False, _(b'resume interrupted graft')),
2824 (b'c', b'continue', False, _(b'resume interrupted graft')),
2825 (b'', b'stop', False, _(b'stop interrupted graft')),
2825 (b'', b'stop', False, _(b'stop interrupted graft')),
2826 (b'', b'abort', False, _(b'abort interrupted graft')),
2826 (b'', b'abort', False, _(b'abort interrupted graft')),
2827 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2827 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2828 (b'', b'log', None, _(b'append graft info to log message')),
2828 (b'', b'log', None, _(b'append graft info to log message')),
2829 (
2829 (
2830 b'',
2830 b'',
2831 b'no-commit',
2831 b'no-commit',
2832 None,
2832 None,
2833 _(b"don't commit, just apply the changes in working directory"),
2833 _(b"don't commit, just apply the changes in working directory"),
2834 ),
2834 ),
2835 (b'f', b'force', False, _(b'force graft')),
2835 (b'f', b'force', False, _(b'force graft')),
2836 (
2836 (
2837 b'D',
2837 b'D',
2838 b'currentdate',
2838 b'currentdate',
2839 False,
2839 False,
2840 _(b'record the current date as commit date'),
2840 _(b'record the current date as commit date'),
2841 ),
2841 ),
2842 (
2842 (
2843 b'U',
2843 b'U',
2844 b'currentuser',
2844 b'currentuser',
2845 False,
2845 False,
2846 _(b'record the current user as committer'),
2846 _(b'record the current user as committer'),
2847 ),
2847 ),
2848 ]
2848 ]
2849 + commitopts2
2849 + commitopts2
2850 + mergetoolopts
2850 + mergetoolopts
2851 + dryrunopts,
2851 + dryrunopts,
2852 _(b'[OPTION]... [-r REV]... REV...'),
2852 _(b'[OPTION]... [-r REV]... REV...'),
2853 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2853 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2854 )
2854 )
2855 def graft(ui, repo, *revs, **opts):
2855 def graft(ui, repo, *revs, **opts):
2856 '''copy changes from other branches onto the current branch
2856 '''copy changes from other branches onto the current branch
2857
2857
2858 This command uses Mercurial's merge logic to copy individual
2858 This command uses Mercurial's merge logic to copy individual
2859 changes from other branches without merging branches in the
2859 changes from other branches without merging branches in the
2860 history graph. This is sometimes known as 'backporting' or
2860 history graph. This is sometimes known as 'backporting' or
2861 'cherry-picking'. By default, graft will copy user, date, and
2861 'cherry-picking'. By default, graft will copy user, date, and
2862 description from the source changesets.
2862 description from the source changesets.
2863
2863
2864 Changesets that are ancestors of the current revision, that have
2864 Changesets that are ancestors of the current revision, that have
2865 already been grafted, or that are merges will be skipped.
2865 already been grafted, or that are merges will be skipped.
2866
2866
2867 If --log is specified, log messages will have a comment appended
2867 If --log is specified, log messages will have a comment appended
2868 of the form::
2868 of the form::
2869
2869
2870 (grafted from CHANGESETHASH)
2870 (grafted from CHANGESETHASH)
2871
2871
2872 If --force is specified, revisions will be grafted even if they
2872 If --force is specified, revisions will be grafted even if they
2873 are already ancestors of, or have been grafted to, the destination.
2873 are already ancestors of, or have been grafted to, the destination.
2874 This is useful when the revisions have since been backed out.
2874 This is useful when the revisions have since been backed out.
2875
2875
2876 If a graft merge results in conflicts, the graft process is
2876 If a graft merge results in conflicts, the graft process is
2877 interrupted so that the current merge can be manually resolved.
2877 interrupted so that the current merge can be manually resolved.
2878 Once all conflicts are addressed, the graft process can be
2878 Once all conflicts are addressed, the graft process can be
2879 continued with the -c/--continue option.
2879 continued with the -c/--continue option.
2880
2880
2881 The -c/--continue option reapplies all the earlier options.
2881 The -c/--continue option reapplies all the earlier options.
2882
2882
2883 .. container:: verbose
2883 .. container:: verbose
2884
2884
2885 The --base option exposes more of how graft internally uses merge with a
2885 The --base option exposes more of how graft internally uses merge with a
2886 custom base revision. --base can be used to specify another ancestor than
2886 custom base revision. --base can be used to specify another ancestor than
2887 the first and only parent.
2887 the first and only parent.
2888
2888
2889 The command::
2889 The command::
2890
2890
2891 hg graft -r 345 --base 234
2891 hg graft -r 345 --base 234
2892
2892
2893 is thus pretty much the same as::
2893 is thus pretty much the same as::
2894
2894
2895 hg diff -r 234 -r 345 | hg import
2895 hg diff -r 234 -r 345 | hg import
2896
2896
2897 but using merge to resolve conflicts and track moved files.
2897 but using merge to resolve conflicts and track moved files.
2898
2898
2899 The result of a merge can thus be backported as a single commit by
2899 The result of a merge can thus be backported as a single commit by
2900 specifying one of the merge parents as base, and thus effectively
2900 specifying one of the merge parents as base, and thus effectively
2901 grafting the changes from the other side.
2901 grafting the changes from the other side.
2902
2902
2903 It is also possible to collapse multiple changesets and clean up history
2903 It is also possible to collapse multiple changesets and clean up history
2904 by specifying another ancestor as base, much like rebase --collapse
2904 by specifying another ancestor as base, much like rebase --collapse
2905 --keep.
2905 --keep.
2906
2906
2907 The commit message can be tweaked after the fact using commit --amend .
2907 The commit message can be tweaked after the fact using commit --amend .
2908
2908
2909 For using non-ancestors as the base to backout changes, see the backout
2909 For using non-ancestors as the base to backout changes, see the backout
2910 command and the hidden --parent option.
2910 command and the hidden --parent option.
2911
2911
2912 .. container:: verbose
2912 .. container:: verbose
2913
2913
2914 Examples:
2914 Examples:
2915
2915
2916 - copy a single change to the stable branch and edit its description::
2916 - copy a single change to the stable branch and edit its description::
2917
2917
2918 hg update stable
2918 hg update stable
2919 hg graft --edit 9393
2919 hg graft --edit 9393
2920
2920
2921 - graft a range of changesets with one exception, updating dates::
2921 - graft a range of changesets with one exception, updating dates::
2922
2922
2923 hg graft -D "2085::2093 and not 2091"
2923 hg graft -D "2085::2093 and not 2091"
2924
2924
2925 - continue a graft after resolving conflicts::
2925 - continue a graft after resolving conflicts::
2926
2926
2927 hg graft -c
2927 hg graft -c
2928
2928
2929 - show the source of a grafted changeset::
2929 - show the source of a grafted changeset::
2930
2930
2931 hg log --debug -r .
2931 hg log --debug -r .
2932
2932
2933 - show revisions sorted by date::
2933 - show revisions sorted by date::
2934
2934
2935 hg log -r "sort(all(), date)"
2935 hg log -r "sort(all(), date)"
2936
2936
2937 - backport the result of a merge as a single commit::
2937 - backport the result of a merge as a single commit::
2938
2938
2939 hg graft -r 123 --base 123^
2939 hg graft -r 123 --base 123^
2940
2940
2941 - land a feature branch as one changeset::
2941 - land a feature branch as one changeset::
2942
2942
2943 hg up -cr default
2943 hg up -cr default
2944 hg graft -r featureX --base "ancestor('featureX', 'default')"
2944 hg graft -r featureX --base "ancestor('featureX', 'default')"
2945
2945
2946 See :hg:`help revisions` for more about specifying revisions.
2946 See :hg:`help revisions` for more about specifying revisions.
2947
2947
2948 Returns 0 on successful completion.
2948 Returns 0 on successful completion.
2949 '''
2949 '''
2950 with repo.wlock():
2950 with repo.wlock():
2951 return _dograft(ui, repo, *revs, **opts)
2951 return _dograft(ui, repo, *revs, **opts)
2952
2952
2953
2953
2954 def _dograft(ui, repo, *revs, **opts):
2954 def _dograft(ui, repo, *revs, **opts):
2955 opts = pycompat.byteskwargs(opts)
2955 opts = pycompat.byteskwargs(opts)
2956 if revs and opts.get(b'rev'):
2956 if revs and opts.get(b'rev'):
2957 ui.warn(
2957 ui.warn(
2958 _(
2958 _(
2959 b'warning: inconsistent use of --rev might give unexpected '
2959 b'warning: inconsistent use of --rev might give unexpected '
2960 b'revision ordering!\n'
2960 b'revision ordering!\n'
2961 )
2961 )
2962 )
2962 )
2963
2963
2964 revs = list(revs)
2964 revs = list(revs)
2965 revs.extend(opts.get(b'rev'))
2965 revs.extend(opts.get(b'rev'))
2966 basectx = None
2966 basectx = None
2967 if opts.get(b'base'):
2967 if opts.get(b'base'):
2968 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2968 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2969 # a dict of data to be stored in state file
2969 # a dict of data to be stored in state file
2970 statedata = {}
2970 statedata = {}
2971 # list of new nodes created by ongoing graft
2971 # list of new nodes created by ongoing graft
2972 statedata[b'newnodes'] = []
2972 statedata[b'newnodes'] = []
2973
2973
2974 if opts.get(b'user') and opts.get(b'currentuser'):
2974 if opts.get(b'user') and opts.get(b'currentuser'):
2975 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
2975 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
2976 if opts.get(b'date') and opts.get(b'currentdate'):
2976 if opts.get(b'date') and opts.get(b'currentdate'):
2977 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
2977 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
2978 if not opts.get(b'user') and opts.get(b'currentuser'):
2978 if not opts.get(b'user') and opts.get(b'currentuser'):
2979 opts[b'user'] = ui.username()
2979 opts[b'user'] = ui.username()
2980 if not opts.get(b'date') and opts.get(b'currentdate'):
2980 if not opts.get(b'date') and opts.get(b'currentdate'):
2981 opts[b'date'] = b"%d %d" % dateutil.makedate()
2981 opts[b'date'] = b"%d %d" % dateutil.makedate()
2982
2982
2983 editor = cmdutil.getcommiteditor(
2983 editor = cmdutil.getcommiteditor(
2984 editform=b'graft', **pycompat.strkwargs(opts)
2984 editform=b'graft', **pycompat.strkwargs(opts)
2985 )
2985 )
2986
2986
2987 cont = False
2987 cont = False
2988 if opts.get(b'no_commit'):
2988 if opts.get(b'no_commit'):
2989 if opts.get(b'edit'):
2989 if opts.get(b'edit'):
2990 raise error.Abort(
2990 raise error.Abort(
2991 _(b"cannot specify --no-commit and --edit together")
2991 _(b"cannot specify --no-commit and --edit together")
2992 )
2992 )
2993 if opts.get(b'currentuser'):
2993 if opts.get(b'currentuser'):
2994 raise error.Abort(
2994 raise error.Abort(
2995 _(b"cannot specify --no-commit and --currentuser together")
2995 _(b"cannot specify --no-commit and --currentuser together")
2996 )
2996 )
2997 if opts.get(b'currentdate'):
2997 if opts.get(b'currentdate'):
2998 raise error.Abort(
2998 raise error.Abort(
2999 _(b"cannot specify --no-commit and --currentdate together")
2999 _(b"cannot specify --no-commit and --currentdate together")
3000 )
3000 )
3001 if opts.get(b'log'):
3001 if opts.get(b'log'):
3002 raise error.Abort(
3002 raise error.Abort(
3003 _(b"cannot specify --no-commit and --log together")
3003 _(b"cannot specify --no-commit and --log together")
3004 )
3004 )
3005
3005
3006 graftstate = statemod.cmdstate(repo, b'graftstate')
3006 graftstate = statemod.cmdstate(repo, b'graftstate')
3007
3007
3008 if opts.get(b'stop'):
3008 if opts.get(b'stop'):
3009 if opts.get(b'continue'):
3009 if opts.get(b'continue'):
3010 raise error.Abort(
3010 raise error.Abort(
3011 _(b"cannot use '--continue' and '--stop' together")
3011 _(b"cannot use '--continue' and '--stop' together")
3012 )
3012 )
3013 if opts.get(b'abort'):
3013 if opts.get(b'abort'):
3014 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3014 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3015
3015
3016 if any(
3016 if any(
3017 (
3017 (
3018 opts.get(b'edit'),
3018 opts.get(b'edit'),
3019 opts.get(b'log'),
3019 opts.get(b'log'),
3020 opts.get(b'user'),
3020 opts.get(b'user'),
3021 opts.get(b'date'),
3021 opts.get(b'date'),
3022 opts.get(b'currentdate'),
3022 opts.get(b'currentdate'),
3023 opts.get(b'currentuser'),
3023 opts.get(b'currentuser'),
3024 opts.get(b'rev'),
3024 opts.get(b'rev'),
3025 )
3025 )
3026 ):
3026 ):
3027 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3027 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3028 return _stopgraft(ui, repo, graftstate)
3028 return _stopgraft(ui, repo, graftstate)
3029 elif opts.get(b'abort'):
3029 elif opts.get(b'abort'):
3030 if opts.get(b'continue'):
3030 if opts.get(b'continue'):
3031 raise error.Abort(
3031 raise error.Abort(
3032 _(b"cannot use '--continue' and '--abort' together")
3032 _(b"cannot use '--continue' and '--abort' together")
3033 )
3033 )
3034 if any(
3034 if any(
3035 (
3035 (
3036 opts.get(b'edit'),
3036 opts.get(b'edit'),
3037 opts.get(b'log'),
3037 opts.get(b'log'),
3038 opts.get(b'user'),
3038 opts.get(b'user'),
3039 opts.get(b'date'),
3039 opts.get(b'date'),
3040 opts.get(b'currentdate'),
3040 opts.get(b'currentdate'),
3041 opts.get(b'currentuser'),
3041 opts.get(b'currentuser'),
3042 opts.get(b'rev'),
3042 opts.get(b'rev'),
3043 )
3043 )
3044 ):
3044 ):
3045 raise error.Abort(
3045 raise error.Abort(
3046 _(b"cannot specify any other flag with '--abort'")
3046 _(b"cannot specify any other flag with '--abort'")
3047 )
3047 )
3048
3048
3049 return cmdutil.abortgraft(ui, repo, graftstate)
3049 return cmdutil.abortgraft(ui, repo, graftstate)
3050 elif opts.get(b'continue'):
3050 elif opts.get(b'continue'):
3051 cont = True
3051 cont = True
3052 if revs:
3052 if revs:
3053 raise error.Abort(_(b"can't specify --continue and revisions"))
3053 raise error.Abort(_(b"can't specify --continue and revisions"))
3054 # read in unfinished revisions
3054 # read in unfinished revisions
3055 if graftstate.exists():
3055 if graftstate.exists():
3056 statedata = cmdutil.readgraftstate(repo, graftstate)
3056 statedata = cmdutil.readgraftstate(repo, graftstate)
3057 if statedata.get(b'date'):
3057 if statedata.get(b'date'):
3058 opts[b'date'] = statedata[b'date']
3058 opts[b'date'] = statedata[b'date']
3059 if statedata.get(b'user'):
3059 if statedata.get(b'user'):
3060 opts[b'user'] = statedata[b'user']
3060 opts[b'user'] = statedata[b'user']
3061 if statedata.get(b'log'):
3061 if statedata.get(b'log'):
3062 opts[b'log'] = True
3062 opts[b'log'] = True
3063 if statedata.get(b'no_commit'):
3063 if statedata.get(b'no_commit'):
3064 opts[b'no_commit'] = statedata.get(b'no_commit')
3064 opts[b'no_commit'] = statedata.get(b'no_commit')
3065 nodes = statedata[b'nodes']
3065 nodes = statedata[b'nodes']
3066 revs = [repo[node].rev() for node in nodes]
3066 revs = [repo[node].rev() for node in nodes]
3067 else:
3067 else:
3068 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3068 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3069 else:
3069 else:
3070 if not revs:
3070 if not revs:
3071 raise error.Abort(_(b'no revisions specified'))
3071 raise error.Abort(_(b'no revisions specified'))
3072 cmdutil.checkunfinished(repo)
3072 cmdutil.checkunfinished(repo)
3073 cmdutil.bailifchanged(repo)
3073 cmdutil.bailifchanged(repo)
3074 revs = scmutil.revrange(repo, revs)
3074 revs = scmutil.revrange(repo, revs)
3075
3075
3076 skipped = set()
3076 skipped = set()
3077 if basectx is None:
3077 if basectx is None:
3078 # check for merges
3078 # check for merges
3079 for rev in repo.revs(b'%ld and merge()', revs):
3079 for rev in repo.revs(b'%ld and merge()', revs):
3080 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3080 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3081 skipped.add(rev)
3081 skipped.add(rev)
3082 revs = [r for r in revs if r not in skipped]
3082 revs = [r for r in revs if r not in skipped]
3083 if not revs:
3083 if not revs:
3084 return -1
3084 return -1
3085 if basectx is not None and len(revs) != 1:
3085 if basectx is not None and len(revs) != 1:
3086 raise error.Abort(_(b'only one revision allowed with --base '))
3086 raise error.Abort(_(b'only one revision allowed with --base '))
3087
3087
3088 # Don't check in the --continue case, in effect retaining --force across
3088 # Don't check in the --continue case, in effect retaining --force across
3089 # --continues. That's because without --force, any revisions we decided to
3089 # --continues. That's because without --force, any revisions we decided to
3090 # skip would have been filtered out here, so they wouldn't have made their
3090 # skip would have been filtered out here, so they wouldn't have made their
3091 # way to the graftstate. With --force, any revisions we would have otherwise
3091 # way to the graftstate. With --force, any revisions we would have otherwise
3092 # skipped would not have been filtered out, and if they hadn't been applied
3092 # skipped would not have been filtered out, and if they hadn't been applied
3093 # already, they'd have been in the graftstate.
3093 # already, they'd have been in the graftstate.
3094 if not (cont or opts.get(b'force')) and basectx is None:
3094 if not (cont or opts.get(b'force')) and basectx is None:
3095 # check for ancestors of dest branch
3095 # check for ancestors of dest branch
3096 crev = repo[b'.'].rev()
3096 crev = repo[b'.'].rev()
3097 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3097 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3098 # XXX make this lazy in the future
3098 # XXX make this lazy in the future
3099 # don't mutate while iterating, create a copy
3099 # don't mutate while iterating, create a copy
3100 for rev in list(revs):
3100 for rev in list(revs):
3101 if rev in ancestors:
3101 if rev in ancestors:
3102 ui.warn(
3102 ui.warn(
3103 _(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev])
3103 _(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev])
3104 )
3104 )
3105 # XXX remove on list is slow
3105 # XXX remove on list is slow
3106 revs.remove(rev)
3106 revs.remove(rev)
3107 if not revs:
3107 if not revs:
3108 return -1
3108 return -1
3109
3109
3110 # analyze revs for earlier grafts
3110 # analyze revs for earlier grafts
3111 ids = {}
3111 ids = {}
3112 for ctx in repo.set(b"%ld", revs):
3112 for ctx in repo.set(b"%ld", revs):
3113 ids[ctx.hex()] = ctx.rev()
3113 ids[ctx.hex()] = ctx.rev()
3114 n = ctx.extra().get(b'source')
3114 n = ctx.extra().get(b'source')
3115 if n:
3115 if n:
3116 ids[n] = ctx.rev()
3116 ids[n] = ctx.rev()
3117
3117
3118 # check ancestors for earlier grafts
3118 # check ancestors for earlier grafts
3119 ui.debug(b'scanning for duplicate grafts\n')
3119 ui.debug(b'scanning for duplicate grafts\n')
3120
3120
3121 # The only changesets we can be sure doesn't contain grafts of any
3121 # The only changesets we can be sure doesn't contain grafts of any
3122 # revs, are the ones that are common ancestors of *all* revs:
3122 # revs, are the ones that are common ancestors of *all* revs:
3123 for rev in repo.revs(b'only(%d,ancestor(%ld))', crev, revs):
3123 for rev in repo.revs(b'only(%d,ancestor(%ld))', crev, revs):
3124 ctx = repo[rev]
3124 ctx = repo[rev]
3125 n = ctx.extra().get(b'source')
3125 n = ctx.extra().get(b'source')
3126 if n in ids:
3126 if n in ids:
3127 try:
3127 try:
3128 r = repo[n].rev()
3128 r = repo[n].rev()
3129 except error.RepoLookupError:
3129 except error.RepoLookupError:
3130 r = None
3130 r = None
3131 if r in revs:
3131 if r in revs:
3132 ui.warn(
3132 ui.warn(
3133 _(
3133 _(
3134 b'skipping revision %d:%s '
3134 b'skipping revision %d:%s '
3135 b'(already grafted to %d:%s)\n'
3135 b'(already grafted to %d:%s)\n'
3136 )
3136 )
3137 % (r, repo[r], rev, ctx)
3137 % (r, repo[r], rev, ctx)
3138 )
3138 )
3139 revs.remove(r)
3139 revs.remove(r)
3140 elif ids[n] in revs:
3140 elif ids[n] in revs:
3141 if r is None:
3141 if r is None:
3142 ui.warn(
3142 ui.warn(
3143 _(
3143 _(
3144 b'skipping already grafted revision %d:%s '
3144 b'skipping already grafted revision %d:%s '
3145 b'(%d:%s also has unknown origin %s)\n'
3145 b'(%d:%s also has unknown origin %s)\n'
3146 )
3146 )
3147 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3147 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3148 )
3148 )
3149 else:
3149 else:
3150 ui.warn(
3150 ui.warn(
3151 _(
3151 _(
3152 b'skipping already grafted revision %d:%s '
3152 b'skipping already grafted revision %d:%s '
3153 b'(%d:%s also has origin %d:%s)\n'
3153 b'(%d:%s also has origin %d:%s)\n'
3154 )
3154 )
3155 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3155 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3156 )
3156 )
3157 revs.remove(ids[n])
3157 revs.remove(ids[n])
3158 elif ctx.hex() in ids:
3158 elif ctx.hex() in ids:
3159 r = ids[ctx.hex()]
3159 r = ids[ctx.hex()]
3160 if r in revs:
3160 if r in revs:
3161 ui.warn(
3161 ui.warn(
3162 _(
3162 _(
3163 b'skipping already grafted revision %d:%s '
3163 b'skipping already grafted revision %d:%s '
3164 b'(was grafted from %d:%s)\n'
3164 b'(was grafted from %d:%s)\n'
3165 )
3165 )
3166 % (r, repo[r], rev, ctx)
3166 % (r, repo[r], rev, ctx)
3167 )
3167 )
3168 revs.remove(r)
3168 revs.remove(r)
3169 if not revs:
3169 if not revs:
3170 return -1
3170 return -1
3171
3171
3172 if opts.get(b'no_commit'):
3172 if opts.get(b'no_commit'):
3173 statedata[b'no_commit'] = True
3173 statedata[b'no_commit'] = True
3174 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3174 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3175 desc = b'%d:%s "%s"' % (
3175 desc = b'%d:%s "%s"' % (
3176 ctx.rev(),
3176 ctx.rev(),
3177 ctx,
3177 ctx,
3178 ctx.description().split(b'\n', 1)[0],
3178 ctx.description().split(b'\n', 1)[0],
3179 )
3179 )
3180 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3180 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3181 if names:
3181 if names:
3182 desc += b' (%s)' % b' '.join(names)
3182 desc += b' (%s)' % b' '.join(names)
3183 ui.status(_(b'grafting %s\n') % desc)
3183 ui.status(_(b'grafting %s\n') % desc)
3184 if opts.get(b'dry_run'):
3184 if opts.get(b'dry_run'):
3185 continue
3185 continue
3186
3186
3187 source = ctx.extra().get(b'source')
3187 source = ctx.extra().get(b'source')
3188 extra = {}
3188 extra = {}
3189 if source:
3189 if source:
3190 extra[b'source'] = source
3190 extra[b'source'] = source
3191 extra[b'intermediate-source'] = ctx.hex()
3191 extra[b'intermediate-source'] = ctx.hex()
3192 else:
3192 else:
3193 extra[b'source'] = ctx.hex()
3193 extra[b'source'] = ctx.hex()
3194 user = ctx.user()
3194 user = ctx.user()
3195 if opts.get(b'user'):
3195 if opts.get(b'user'):
3196 user = opts[b'user']
3196 user = opts[b'user']
3197 statedata[b'user'] = user
3197 statedata[b'user'] = user
3198 date = ctx.date()
3198 date = ctx.date()
3199 if opts.get(b'date'):
3199 if opts.get(b'date'):
3200 date = opts[b'date']
3200 date = opts[b'date']
3201 statedata[b'date'] = date
3201 statedata[b'date'] = date
3202 message = ctx.description()
3202 message = ctx.description()
3203 if opts.get(b'log'):
3203 if opts.get(b'log'):
3204 message += b'\n(grafted from %s)' % ctx.hex()
3204 message += b'\n(grafted from %s)' % ctx.hex()
3205 statedata[b'log'] = True
3205 statedata[b'log'] = True
3206
3206
3207 # we don't merge the first commit when continuing
3207 # we don't merge the first commit when continuing
3208 if not cont:
3208 if not cont:
3209 # perform the graft merge with p1(rev) as 'ancestor'
3209 # perform the graft merge with p1(rev) as 'ancestor'
3210 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3210 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3211 base = ctx.p1() if basectx is None else basectx
3211 base = ctx.p1() if basectx is None else basectx
3212 with ui.configoverride(overrides, b'graft'):
3212 with ui.configoverride(overrides, b'graft'):
3213 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3213 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3214 # report any conflicts
3214 # report any conflicts
3215 if stats.unresolvedcount > 0:
3215 if stats.unresolvedcount > 0:
3216 # write out state for --continue
3216 # write out state for --continue
3217 nodes = [repo[rev].hex() for rev in revs[pos:]]
3217 nodes = [repo[rev].hex() for rev in revs[pos:]]
3218 statedata[b'nodes'] = nodes
3218 statedata[b'nodes'] = nodes
3219 stateversion = 1
3219 stateversion = 1
3220 graftstate.save(stateversion, statedata)
3220 graftstate.save(stateversion, statedata)
3221 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3221 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3222 raise error.Abort(
3222 raise error.Abort(
3223 _(b"unresolved conflicts, can't continue"), hint=hint
3223 _(b"unresolved conflicts, can't continue"), hint=hint
3224 )
3224 )
3225 else:
3225 else:
3226 cont = False
3226 cont = False
3227
3227
3228 # commit if --no-commit is false
3228 # commit if --no-commit is false
3229 if not opts.get(b'no_commit'):
3229 if not opts.get(b'no_commit'):
3230 node = repo.commit(
3230 node = repo.commit(
3231 text=message, user=user, date=date, extra=extra, editor=editor
3231 text=message, user=user, date=date, extra=extra, editor=editor
3232 )
3232 )
3233 if node is None:
3233 if node is None:
3234 ui.warn(
3234 ui.warn(
3235 _(b'note: graft of %d:%s created no changes to commit\n')
3235 _(b'note: graft of %d:%s created no changes to commit\n')
3236 % (ctx.rev(), ctx)
3236 % (ctx.rev(), ctx)
3237 )
3237 )
3238 # checking that newnodes exist because old state files won't have it
3238 # checking that newnodes exist because old state files won't have it
3239 elif statedata.get(b'newnodes') is not None:
3239 elif statedata.get(b'newnodes') is not None:
3240 statedata[b'newnodes'].append(node)
3240 statedata[b'newnodes'].append(node)
3241
3241
3242 # remove state when we complete successfully
3242 # remove state when we complete successfully
3243 if not opts.get(b'dry_run'):
3243 if not opts.get(b'dry_run'):
3244 graftstate.delete()
3244 graftstate.delete()
3245
3245
3246 return 0
3246 return 0
3247
3247
3248
3248
3249 def _stopgraft(ui, repo, graftstate):
3249 def _stopgraft(ui, repo, graftstate):
3250 """stop the interrupted graft"""
3250 """stop the interrupted graft"""
3251 if not graftstate.exists():
3251 if not graftstate.exists():
3252 raise error.Abort(_(b"no interrupted graft found"))
3252 raise error.Abort(_(b"no interrupted graft found"))
3253 pctx = repo[b'.']
3253 pctx = repo[b'.']
3254 hg.updaterepo(repo, pctx.node(), overwrite=True)
3254 hg.updaterepo(repo, pctx.node(), overwrite=True)
3255 graftstate.delete()
3255 graftstate.delete()
3256 ui.status(_(b"stopped the interrupted graft\n"))
3256 ui.status(_(b"stopped the interrupted graft\n"))
3257 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3257 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3258 return 0
3258 return 0
3259
3259
3260
3260
3261 statemod.addunfinished(
3261 statemod.addunfinished(
3262 b'graft',
3262 b'graft',
3263 fname=b'graftstate',
3263 fname=b'graftstate',
3264 clearable=True,
3264 clearable=True,
3265 stopflag=True,
3265 stopflag=True,
3266 continueflag=True,
3266 continueflag=True,
3267 abortfunc=cmdutil.hgabortgraft,
3267 abortfunc=cmdutil.hgabortgraft,
3268 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3268 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3269 )
3269 )
3270
3270
3271
3271
3272 @command(
3272 @command(
3273 b'grep',
3273 b'grep',
3274 [
3274 [
3275 (b'0', b'print0', None, _(b'end fields with NUL')),
3275 (b'0', b'print0', None, _(b'end fields with NUL')),
3276 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3276 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3277 (
3277 (
3278 b'',
3278 b'',
3279 b'diff',
3279 b'diff',
3280 None,
3280 None,
3281 _(
3281 _(
3282 b'search revision differences for when the pattern was added '
3282 b'search revision differences for when the pattern was added '
3283 b'or removed'
3283 b'or removed'
3284 ),
3284 ),
3285 ),
3285 ),
3286 (b'a', b'text', None, _(b'treat all files as text')),
3286 (b'a', b'text', None, _(b'treat all files as text')),
3287 (
3287 (
3288 b'f',
3288 b'f',
3289 b'follow',
3289 b'follow',
3290 None,
3290 None,
3291 _(
3291 _(
3292 b'follow changeset history,'
3292 b'follow changeset history,'
3293 b' or file history across copies and renames'
3293 b' or file history across copies and renames'
3294 ),
3294 ),
3295 ),
3295 ),
3296 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3296 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3297 (
3297 (
3298 b'l',
3298 b'l',
3299 b'files-with-matches',
3299 b'files-with-matches',
3300 None,
3300 None,
3301 _(b'print only filenames and revisions that match'),
3301 _(b'print only filenames and revisions that match'),
3302 ),
3302 ),
3303 (b'n', b'line-number', None, _(b'print matching line numbers')),
3303 (b'n', b'line-number', None, _(b'print matching line numbers')),
3304 (
3304 (
3305 b'r',
3305 b'r',
3306 b'rev',
3306 b'rev',
3307 [],
3307 [],
3308 _(b'search files changed within revision range'),
3308 _(b'search files changed within revision range'),
3309 _(b'REV'),
3309 _(b'REV'),
3310 ),
3310 ),
3311 (
3311 (
3312 b'',
3312 b'',
3313 b'all-files',
3313 b'all-files',
3314 None,
3314 None,
3315 _(
3315 _(
3316 b'include all files in the changeset while grepping (DEPRECATED)'
3316 b'include all files in the changeset while grepping (DEPRECATED)'
3317 ),
3317 ),
3318 ),
3318 ),
3319 (b'u', b'user', None, _(b'list the author (long with -v)')),
3319 (b'u', b'user', None, _(b'list the author (long with -v)')),
3320 (b'd', b'date', None, _(b'list the date (short with -q)')),
3320 (b'd', b'date', None, _(b'list the date (short with -q)')),
3321 ]
3321 ]
3322 + formatteropts
3322 + formatteropts
3323 + walkopts,
3323 + walkopts,
3324 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3324 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3325 helpcategory=command.CATEGORY_FILE_CONTENTS,
3325 helpcategory=command.CATEGORY_FILE_CONTENTS,
3326 inferrepo=True,
3326 inferrepo=True,
3327 intents={INTENT_READONLY},
3327 intents={INTENT_READONLY},
3328 )
3328 )
3329 def grep(ui, repo, pattern, *pats, **opts):
3329 def grep(ui, repo, pattern, *pats, **opts):
3330 """search for a pattern in specified files
3330 """search for a pattern in specified files
3331
3331
3332 Search the working directory or revision history for a regular
3332 Search the working directory or revision history for a regular
3333 expression in the specified files for the entire repository.
3333 expression in the specified files for the entire repository.
3334
3334
3335 By default, grep searches the repository files in the working
3335 By default, grep searches the repository files in the working
3336 directory and prints the files where it finds a match. To specify
3336 directory and prints the files where it finds a match. To specify
3337 historical revisions instead of the working directory, use the
3337 historical revisions instead of the working directory, use the
3338 --rev flag.
3338 --rev flag.
3339
3339
3340 To search instead historical revision differences that contains a
3340 To search instead historical revision differences that contains a
3341 change in match status ("-" for a match that becomes a non-match,
3341 change in match status ("-" for a match that becomes a non-match,
3342 or "+" for a non-match that becomes a match), use the --diff flag.
3342 or "+" for a non-match that becomes a match), use the --diff flag.
3343
3343
3344 PATTERN can be any Python (roughly Perl-compatible) regular
3344 PATTERN can be any Python (roughly Perl-compatible) regular
3345 expression.
3345 expression.
3346
3346
3347 If no FILEs are specified and the --rev flag isn't supplied, all
3347 If no FILEs are specified and the --rev flag isn't supplied, all
3348 files in the working directory are searched. When using the --rev
3348 files in the working directory are searched. When using the --rev
3349 flag and specifying FILEs, use the --follow argument to also
3349 flag and specifying FILEs, use the --follow argument to also
3350 follow the specified FILEs across renames and copies.
3350 follow the specified FILEs across renames and copies.
3351
3351
3352 .. container:: verbose
3352 .. container:: verbose
3353
3353
3354 Template:
3354 Template:
3355
3355
3356 The following keywords are supported in addition to the common template
3356 The following keywords are supported in addition to the common template
3357 keywords and functions. See also :hg:`help templates`.
3357 keywords and functions. See also :hg:`help templates`.
3358
3358
3359 :change: String. Character denoting insertion ``+`` or removal ``-``.
3359 :change: String. Character denoting insertion ``+`` or removal ``-``.
3360 Available if ``--diff`` is specified.
3360 Available if ``--diff`` is specified.
3361 :lineno: Integer. Line number of the match.
3361 :lineno: Integer. Line number of the match.
3362 :path: String. Repository-absolute path of the file.
3362 :path: String. Repository-absolute path of the file.
3363 :texts: List of text chunks.
3363 :texts: List of text chunks.
3364
3364
3365 And each entry of ``{texts}`` provides the following sub-keywords.
3365 And each entry of ``{texts}`` provides the following sub-keywords.
3366
3366
3367 :matched: Boolean. True if the chunk matches the specified pattern.
3367 :matched: Boolean. True if the chunk matches the specified pattern.
3368 :text: String. Chunk content.
3368 :text: String. Chunk content.
3369
3369
3370 See :hg:`help templates.operators` for the list expansion syntax.
3370 See :hg:`help templates.operators` for the list expansion syntax.
3371
3371
3372 Returns 0 if a match is found, 1 otherwise.
3372 Returns 0 if a match is found, 1 otherwise.
3373
3373
3374 """
3374 """
3375 opts = pycompat.byteskwargs(opts)
3375 opts = pycompat.byteskwargs(opts)
3376 diff = opts.get(b'all') or opts.get(b'diff')
3376 diff = opts.get(b'all') or opts.get(b'diff')
3377 if diff and opts.get(b'all_files'):
3377 if diff and opts.get(b'all_files'):
3378 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3378 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3379 if opts.get(b'all_files') is None and not diff:
3379 if opts.get(b'all_files') is None and not diff:
3380 opts[b'all_files'] = True
3380 opts[b'all_files'] = True
3381 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3381 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3382 all_files = opts.get(b'all_files')
3382 all_files = opts.get(b'all_files')
3383 if plaingrep:
3383 if plaingrep:
3384 opts[b'rev'] = [b'wdir()']
3384 opts[b'rev'] = [b'wdir()']
3385
3385
3386 reflags = re.M
3386 reflags = re.M
3387 if opts.get(b'ignore_case'):
3387 if opts.get(b'ignore_case'):
3388 reflags |= re.I
3388 reflags |= re.I
3389 try:
3389 try:
3390 regexp = util.re.compile(pattern, reflags)
3390 regexp = util.re.compile(pattern, reflags)
3391 except re.error as inst:
3391 except re.error as inst:
3392 ui.warn(
3392 ui.warn(
3393 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3393 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3394 )
3394 )
3395 return 1
3395 return 1
3396 sep, eol = b':', b'\n'
3396 sep, eol = b':', b'\n'
3397 if opts.get(b'print0'):
3397 if opts.get(b'print0'):
3398 sep = eol = b'\0'
3398 sep = eol = b'\0'
3399
3399
3400 getfile = util.lrucachefunc(repo.file)
3400 getfile = util.lrucachefunc(repo.file)
3401
3401
3402 def matchlines(body):
3402 def matchlines(body):
3403 begin = 0
3403 begin = 0
3404 linenum = 0
3404 linenum = 0
3405 while begin < len(body):
3405 while begin < len(body):
3406 match = regexp.search(body, begin)
3406 match = regexp.search(body, begin)
3407 if not match:
3407 if not match:
3408 break
3408 break
3409 mstart, mend = match.span()
3409 mstart, mend = match.span()
3410 linenum += body.count(b'\n', begin, mstart) + 1
3410 linenum += body.count(b'\n', begin, mstart) + 1
3411 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3411 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3412 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3412 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3413 lend = begin - 1
3413 lend = begin - 1
3414 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3414 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3415
3415
3416 class linestate(object):
3416 class linestate(object):
3417 def __init__(self, line, linenum, colstart, colend):
3417 def __init__(self, line, linenum, colstart, colend):
3418 self.line = line
3418 self.line = line
3419 self.linenum = linenum
3419 self.linenum = linenum
3420 self.colstart = colstart
3420 self.colstart = colstart
3421 self.colend = colend
3421 self.colend = colend
3422
3422
3423 def __hash__(self):
3423 def __hash__(self):
3424 return hash((self.linenum, self.line))
3424 return hash((self.linenum, self.line))
3425
3425
3426 def __eq__(self, other):
3426 def __eq__(self, other):
3427 return self.line == other.line
3427 return self.line == other.line
3428
3428
3429 def findpos(self):
3429 def findpos(self):
3430 """Iterate all (start, end) indices of matches"""
3430 """Iterate all (start, end) indices of matches"""
3431 yield self.colstart, self.colend
3431 yield self.colstart, self.colend
3432 p = self.colend
3432 p = self.colend
3433 while p < len(self.line):
3433 while p < len(self.line):
3434 m = regexp.search(self.line, p)
3434 m = regexp.search(self.line, p)
3435 if not m:
3435 if not m:
3436 break
3436 break
3437 yield m.span()
3437 yield m.span()
3438 p = m.end()
3438 p = m.end()
3439
3439
3440 matches = {}
3440 matches = {}
3441 copies = {}
3441 copies = {}
3442
3442
3443 def grepbody(fn, rev, body):
3443 def grepbody(fn, rev, body):
3444 matches[rev].setdefault(fn, [])
3444 matches[rev].setdefault(fn, [])
3445 m = matches[rev][fn]
3445 m = matches[rev][fn]
3446 if body is None:
3446 if body is None:
3447 return
3447 return
3448
3448
3449 for lnum, cstart, cend, line in matchlines(body):
3449 for lnum, cstart, cend, line in matchlines(body):
3450 s = linestate(line, lnum, cstart, cend)
3450 s = linestate(line, lnum, cstart, cend)
3451 m.append(s)
3451 m.append(s)
3452
3452
3453 def difflinestates(a, b):
3453 def difflinestates(a, b):
3454 sm = difflib.SequenceMatcher(None, a, b)
3454 sm = difflib.SequenceMatcher(None, a, b)
3455 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3455 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3456 if tag == 'insert':
3456 if tag == 'insert':
3457 for i in pycompat.xrange(blo, bhi):
3457 for i in pycompat.xrange(blo, bhi):
3458 yield (b'+', b[i])
3458 yield (b'+', b[i])
3459 elif tag == 'delete':
3459 elif tag == 'delete':
3460 for i in pycompat.xrange(alo, ahi):
3460 for i in pycompat.xrange(alo, ahi):
3461 yield (b'-', a[i])
3461 yield (b'-', a[i])
3462 elif tag == 'replace':
3462 elif tag == 'replace':
3463 for i in pycompat.xrange(alo, ahi):
3463 for i in pycompat.xrange(alo, ahi):
3464 yield (b'-', a[i])
3464 yield (b'-', a[i])
3465 for i in pycompat.xrange(blo, bhi):
3465 for i in pycompat.xrange(blo, bhi):
3466 yield (b'+', b[i])
3466 yield (b'+', b[i])
3467
3467
3468 uipathfn = scmutil.getuipathfn(repo)
3468 uipathfn = scmutil.getuipathfn(repo)
3469
3469
3470 def display(fm, fn, ctx, pstates, states):
3470 def display(fm, fn, ctx, pstates, states):
3471 rev = scmutil.intrev(ctx)
3471 rev = scmutil.intrev(ctx)
3472 if fm.isplain():
3472 if fm.isplain():
3473 formatuser = ui.shortuser
3473 formatuser = ui.shortuser
3474 else:
3474 else:
3475 formatuser = pycompat.bytestr
3475 formatuser = pycompat.bytestr
3476 if ui.quiet:
3476 if ui.quiet:
3477 datefmt = b'%Y-%m-%d'
3477 datefmt = b'%Y-%m-%d'
3478 else:
3478 else:
3479 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3479 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3480 found = False
3480 found = False
3481
3481
3482 @util.cachefunc
3482 @util.cachefunc
3483 def binary():
3483 def binary():
3484 flog = getfile(fn)
3484 flog = getfile(fn)
3485 try:
3485 try:
3486 return stringutil.binary(flog.read(ctx.filenode(fn)))
3486 return stringutil.binary(flog.read(ctx.filenode(fn)))
3487 except error.WdirUnsupported:
3487 except error.WdirUnsupported:
3488 return ctx[fn].isbinary()
3488 return ctx[fn].isbinary()
3489
3489
3490 fieldnamemap = {b'linenumber': b'lineno'}
3490 fieldnamemap = {b'linenumber': b'lineno'}
3491 if diff:
3491 if diff:
3492 iter = difflinestates(pstates, states)
3492 iter = difflinestates(pstates, states)
3493 else:
3493 else:
3494 iter = [(b'', l) for l in states]
3494 iter = [(b'', l) for l in states]
3495 for change, l in iter:
3495 for change, l in iter:
3496 fm.startitem()
3496 fm.startitem()
3497 fm.context(ctx=ctx)
3497 fm.context(ctx=ctx)
3498 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3498 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3499 fm.plain(uipathfn(fn), label=b'grep.filename')
3499 fm.plain(uipathfn(fn), label=b'grep.filename')
3500
3500
3501 cols = [
3501 cols = [
3502 (b'rev', b'%d', rev, not plaingrep, b''),
3502 (b'rev', b'%d', rev, not plaingrep, b''),
3503 (
3503 (
3504 b'linenumber',
3504 b'linenumber',
3505 b'%d',
3505 b'%d',
3506 l.linenum,
3506 l.linenum,
3507 opts.get(b'line_number'),
3507 opts.get(b'line_number'),
3508 b'',
3508 b'',
3509 ),
3509 ),
3510 ]
3510 ]
3511 if diff:
3511 if diff:
3512 cols.append(
3512 cols.append(
3513 (
3513 (
3514 b'change',
3514 b'change',
3515 b'%s',
3515 b'%s',
3516 change,
3516 change,
3517 True,
3517 True,
3518 b'grep.inserted '
3518 b'grep.inserted '
3519 if change == b'+'
3519 if change == b'+'
3520 else b'grep.deleted ',
3520 else b'grep.deleted ',
3521 )
3521 )
3522 )
3522 )
3523 cols.extend(
3523 cols.extend(
3524 [
3524 [
3525 (
3525 (
3526 b'user',
3526 b'user',
3527 b'%s',
3527 b'%s',
3528 formatuser(ctx.user()),
3528 formatuser(ctx.user()),
3529 opts.get(b'user'),
3529 opts.get(b'user'),
3530 b'',
3530 b'',
3531 ),
3531 ),
3532 (
3532 (
3533 b'date',
3533 b'date',
3534 b'%s',
3534 b'%s',
3535 fm.formatdate(ctx.date(), datefmt),
3535 fm.formatdate(ctx.date(), datefmt),
3536 opts.get(b'date'),
3536 opts.get(b'date'),
3537 b'',
3537 b'',
3538 ),
3538 ),
3539 ]
3539 ]
3540 )
3540 )
3541 for name, fmt, data, cond, extra_label in cols:
3541 for name, fmt, data, cond, extra_label in cols:
3542 if cond:
3542 if cond:
3543 fm.plain(sep, label=b'grep.sep')
3543 fm.plain(sep, label=b'grep.sep')
3544 field = fieldnamemap.get(name, name)
3544 field = fieldnamemap.get(name, name)
3545 label = extra_label + (b'grep.%s' % name)
3545 label = extra_label + (b'grep.%s' % name)
3546 fm.condwrite(cond, field, fmt, data, label=label)
3546 fm.condwrite(cond, field, fmt, data, label=label)
3547 if not opts.get(b'files_with_matches'):
3547 if not opts.get(b'files_with_matches'):
3548 fm.plain(sep, label=b'grep.sep')
3548 fm.plain(sep, label=b'grep.sep')
3549 if not opts.get(b'text') and binary():
3549 if not opts.get(b'text') and binary():
3550 fm.plain(_(b" Binary file matches"))
3550 fm.plain(_(b" Binary file matches"))
3551 else:
3551 else:
3552 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3552 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3553 fm.plain(eol)
3553 fm.plain(eol)
3554 found = True
3554 found = True
3555 if opts.get(b'files_with_matches'):
3555 if opts.get(b'files_with_matches'):
3556 break
3556 break
3557 return found
3557 return found
3558
3558
3559 def displaymatches(fm, l):
3559 def displaymatches(fm, l):
3560 p = 0
3560 p = 0
3561 for s, e in l.findpos():
3561 for s, e in l.findpos():
3562 if p < s:
3562 if p < s:
3563 fm.startitem()
3563 fm.startitem()
3564 fm.write(b'text', b'%s', l.line[p:s])
3564 fm.write(b'text', b'%s', l.line[p:s])
3565 fm.data(matched=False)
3565 fm.data(matched=False)
3566 fm.startitem()
3566 fm.startitem()
3567 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3567 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3568 fm.data(matched=True)
3568 fm.data(matched=True)
3569 p = e
3569 p = e
3570 if p < len(l.line):
3570 if p < len(l.line):
3571 fm.startitem()
3571 fm.startitem()
3572 fm.write(b'text', b'%s', l.line[p:])
3572 fm.write(b'text', b'%s', l.line[p:])
3573 fm.data(matched=False)
3573 fm.data(matched=False)
3574 fm.end()
3574 fm.end()
3575
3575
3576 skip = set()
3576 skip = set()
3577 revfiles = {}
3577 revfiles = {}
3578 match = scmutil.match(repo[None], pats, opts)
3578 match = scmutil.match(repo[None], pats, opts)
3579 found = False
3579 found = False
3580 follow = opts.get(b'follow')
3580 follow = opts.get(b'follow')
3581
3581
3582 getrenamed = scmutil.getrenamedfn(repo)
3582 getrenamed = scmutil.getrenamedfn(repo)
3583
3583
3584 def get_file_content(filename, filelog, filenode, context, revision):
3584 def get_file_content(filename, filelog, filenode, context, revision):
3585 try:
3585 try:
3586 content = filelog.read(filenode)
3586 content = filelog.read(filenode)
3587 except error.WdirUnsupported:
3587 except error.WdirUnsupported:
3588 content = context[filename].data()
3588 content = context[filename].data()
3589 except error.CensoredNodeError:
3589 except error.CensoredNodeError:
3590 content = None
3590 content = None
3591 ui.warn(
3591 ui.warn(
3592 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3592 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3593 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3593 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3594 )
3594 )
3595 return content
3595 return content
3596
3596
3597 def prep(ctx, fns):
3597 def prep(ctx, fns):
3598 rev = ctx.rev()
3598 rev = ctx.rev()
3599 pctx = ctx.p1()
3599 pctx = ctx.p1()
3600 parent = pctx.rev()
3600 parent = pctx.rev()
3601 matches.setdefault(rev, {})
3601 matches.setdefault(rev, {})
3602 matches.setdefault(parent, {})
3602 matches.setdefault(parent, {})
3603 files = revfiles.setdefault(rev, [])
3603 files = revfiles.setdefault(rev, [])
3604 for fn in fns:
3604 for fn in fns:
3605 flog = getfile(fn)
3605 flog = getfile(fn)
3606 try:
3606 try:
3607 fnode = ctx.filenode(fn)
3607 fnode = ctx.filenode(fn)
3608 except error.LookupError:
3608 except error.LookupError:
3609 continue
3609 continue
3610
3610
3611 copy = None
3611 copy = None
3612 if follow:
3612 if follow:
3613 copy = getrenamed(fn, rev)
3613 copy = getrenamed(fn, rev)
3614 if copy:
3614 if copy:
3615 copies.setdefault(rev, {})[fn] = copy
3615 copies.setdefault(rev, {})[fn] = copy
3616 if fn in skip:
3616 if fn in skip:
3617 skip.add(copy)
3617 skip.add(copy)
3618 if fn in skip:
3618 if fn in skip:
3619 continue
3619 continue
3620 files.append(fn)
3620 files.append(fn)
3621
3621
3622 if fn not in matches[rev]:
3622 if fn not in matches[rev]:
3623 content = get_file_content(fn, flog, fnode, ctx, rev)
3623 content = get_file_content(fn, flog, fnode, ctx, rev)
3624 grepbody(fn, rev, content)
3624 grepbody(fn, rev, content)
3625
3625
3626 pfn = copy or fn
3626 pfn = copy or fn
3627 if pfn not in matches[parent]:
3627 if pfn not in matches[parent]:
3628 try:
3628 try:
3629 pfnode = pctx.filenode(pfn)
3629 pfnode = pctx.filenode(pfn)
3630 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3630 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3631 grepbody(pfn, parent, pcontent)
3631 grepbody(pfn, parent, pcontent)
3632 except error.LookupError:
3632 except error.LookupError:
3633 pass
3633 pass
3634
3634
3635 ui.pager(b'grep')
3635 ui.pager(b'grep')
3636 fm = ui.formatter(b'grep', opts)
3636 fm = ui.formatter(b'grep', opts)
3637 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3637 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3638 rev = ctx.rev()
3638 rev = ctx.rev()
3639 parent = ctx.p1().rev()
3639 parent = ctx.p1().rev()
3640 for fn in sorted(revfiles.get(rev, [])):
3640 for fn in sorted(revfiles.get(rev, [])):
3641 states = matches[rev][fn]
3641 states = matches[rev][fn]
3642 copy = copies.get(rev, {}).get(fn)
3642 copy = copies.get(rev, {}).get(fn)
3643 if fn in skip:
3643 if fn in skip:
3644 if copy:
3644 if copy:
3645 skip.add(copy)
3645 skip.add(copy)
3646 continue
3646 continue
3647 pstates = matches.get(parent, {}).get(copy or fn, [])
3647 pstates = matches.get(parent, {}).get(copy or fn, [])
3648 if pstates or states:
3648 if pstates or states:
3649 r = display(fm, fn, ctx, pstates, states)
3649 r = display(fm, fn, ctx, pstates, states)
3650 found = found or r
3650 found = found or r
3651 if r and not diff and not all_files:
3651 if r and not diff and not all_files:
3652 skip.add(fn)
3652 skip.add(fn)
3653 if copy:
3653 if copy:
3654 skip.add(copy)
3654 skip.add(copy)
3655 del revfiles[rev]
3655 del revfiles[rev]
3656 # We will keep the matches dict for the duration of the window
3656 # We will keep the matches dict for the duration of the window
3657 # clear the matches dict once the window is over
3657 # clear the matches dict once the window is over
3658 if not revfiles:
3658 if not revfiles:
3659 matches.clear()
3659 matches.clear()
3660 fm.end()
3660 fm.end()
3661
3661
3662 return not found
3662 return not found
3663
3663
3664
3664
3665 @command(
3665 @command(
3666 b'heads',
3666 b'heads',
3667 [
3667 [
3668 (
3668 (
3669 b'r',
3669 b'r',
3670 b'rev',
3670 b'rev',
3671 b'',
3671 b'',
3672 _(b'show only heads which are descendants of STARTREV'),
3672 _(b'show only heads which are descendants of STARTREV'),
3673 _(b'STARTREV'),
3673 _(b'STARTREV'),
3674 ),
3674 ),
3675 (b't', b'topo', False, _(b'show topological heads only')),
3675 (b't', b'topo', False, _(b'show topological heads only')),
3676 (
3676 (
3677 b'a',
3677 b'a',
3678 b'active',
3678 b'active',
3679 False,
3679 False,
3680 _(b'show active branchheads only (DEPRECATED)'),
3680 _(b'show active branchheads only (DEPRECATED)'),
3681 ),
3681 ),
3682 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3682 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3683 ]
3683 ]
3684 + templateopts,
3684 + templateopts,
3685 _(b'[-ct] [-r STARTREV] [REV]...'),
3685 _(b'[-ct] [-r STARTREV] [REV]...'),
3686 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3686 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3687 intents={INTENT_READONLY},
3687 intents={INTENT_READONLY},
3688 )
3688 )
3689 def heads(ui, repo, *branchrevs, **opts):
3689 def heads(ui, repo, *branchrevs, **opts):
3690 """show branch heads
3690 """show branch heads
3691
3691
3692 With no arguments, show all open branch heads in the repository.
3692 With no arguments, show all open branch heads in the repository.
3693 Branch heads are changesets that have no descendants on the
3693 Branch heads are changesets that have no descendants on the
3694 same branch. They are where development generally takes place and
3694 same branch. They are where development generally takes place and
3695 are the usual targets for update and merge operations.
3695 are the usual targets for update and merge operations.
3696
3696
3697 If one or more REVs are given, only open branch heads on the
3697 If one or more REVs are given, only open branch heads on the
3698 branches associated with the specified changesets are shown. This
3698 branches associated with the specified changesets are shown. This
3699 means that you can use :hg:`heads .` to see the heads on the
3699 means that you can use :hg:`heads .` to see the heads on the
3700 currently checked-out branch.
3700 currently checked-out branch.
3701
3701
3702 If -c/--closed is specified, also show branch heads marked closed
3702 If -c/--closed is specified, also show branch heads marked closed
3703 (see :hg:`commit --close-branch`).
3703 (see :hg:`commit --close-branch`).
3704
3704
3705 If STARTREV is specified, only those heads that are descendants of
3705 If STARTREV is specified, only those heads that are descendants of
3706 STARTREV will be displayed.
3706 STARTREV will be displayed.
3707
3707
3708 If -t/--topo is specified, named branch mechanics will be ignored and only
3708 If -t/--topo is specified, named branch mechanics will be ignored and only
3709 topological heads (changesets with no children) will be shown.
3709 topological heads (changesets with no children) will be shown.
3710
3710
3711 Returns 0 if matching heads are found, 1 if not.
3711 Returns 0 if matching heads are found, 1 if not.
3712 """
3712 """
3713
3713
3714 opts = pycompat.byteskwargs(opts)
3714 opts = pycompat.byteskwargs(opts)
3715 start = None
3715 start = None
3716 rev = opts.get(b'rev')
3716 rev = opts.get(b'rev')
3717 if rev:
3717 if rev:
3718 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3718 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3719 start = scmutil.revsingle(repo, rev, None).node()
3719 start = scmutil.revsingle(repo, rev, None).node()
3720
3720
3721 if opts.get(b'topo'):
3721 if opts.get(b'topo'):
3722 heads = [repo[h] for h in repo.heads(start)]
3722 heads = [repo[h] for h in repo.heads(start)]
3723 else:
3723 else:
3724 heads = []
3724 heads = []
3725 for branch in repo.branchmap():
3725 for branch in repo.branchmap():
3726 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3726 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3727 heads = [repo[h] for h in heads]
3727 heads = [repo[h] for h in heads]
3728
3728
3729 if branchrevs:
3729 if branchrevs:
3730 branches = set(
3730 branches = set(
3731 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3731 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3732 )
3732 )
3733 heads = [h for h in heads if h.branch() in branches]
3733 heads = [h for h in heads if h.branch() in branches]
3734
3734
3735 if opts.get(b'active') and branchrevs:
3735 if opts.get(b'active') and branchrevs:
3736 dagheads = repo.heads(start)
3736 dagheads = repo.heads(start)
3737 heads = [h for h in heads if h.node() in dagheads]
3737 heads = [h for h in heads if h.node() in dagheads]
3738
3738
3739 if branchrevs:
3739 if branchrevs:
3740 haveheads = set(h.branch() for h in heads)
3740 haveheads = set(h.branch() for h in heads)
3741 if branches - haveheads:
3741 if branches - haveheads:
3742 headless = b', '.join(b for b in branches - haveheads)
3742 headless = b', '.join(b for b in branches - haveheads)
3743 msg = _(b'no open branch heads found on branches %s')
3743 msg = _(b'no open branch heads found on branches %s')
3744 if opts.get(b'rev'):
3744 if opts.get(b'rev'):
3745 msg += _(b' (started at %s)') % opts[b'rev']
3745 msg += _(b' (started at %s)') % opts[b'rev']
3746 ui.warn((msg + b'\n') % headless)
3746 ui.warn((msg + b'\n') % headless)
3747
3747
3748 if not heads:
3748 if not heads:
3749 return 1
3749 return 1
3750
3750
3751 ui.pager(b'heads')
3751 ui.pager(b'heads')
3752 heads = sorted(heads, key=lambda x: -(x.rev()))
3752 heads = sorted(heads, key=lambda x: -(x.rev()))
3753 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3753 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3754 for ctx in heads:
3754 for ctx in heads:
3755 displayer.show(ctx)
3755 displayer.show(ctx)
3756 displayer.close()
3756 displayer.close()
3757
3757
3758
3758
3759 @command(
3759 @command(
3760 b'help',
3760 b'help',
3761 [
3761 [
3762 (b'e', b'extension', None, _(b'show only help for extensions')),
3762 (b'e', b'extension', None, _(b'show only help for extensions')),
3763 (b'c', b'command', None, _(b'show only help for commands')),
3763 (b'c', b'command', None, _(b'show only help for commands')),
3764 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3764 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3765 (
3765 (
3766 b's',
3766 b's',
3767 b'system',
3767 b'system',
3768 [],
3768 [],
3769 _(b'show help for specific platform(s)'),
3769 _(b'show help for specific platform(s)'),
3770 _(b'PLATFORM'),
3770 _(b'PLATFORM'),
3771 ),
3771 ),
3772 ],
3772 ],
3773 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3773 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3774 helpcategory=command.CATEGORY_HELP,
3774 helpcategory=command.CATEGORY_HELP,
3775 norepo=True,
3775 norepo=True,
3776 intents={INTENT_READONLY},
3776 intents={INTENT_READONLY},
3777 )
3777 )
3778 def help_(ui, name=None, **opts):
3778 def help_(ui, name=None, **opts):
3779 """show help for a given topic or a help overview
3779 """show help for a given topic or a help overview
3780
3780
3781 With no arguments, print a list of commands with short help messages.
3781 With no arguments, print a list of commands with short help messages.
3782
3782
3783 Given a topic, extension, or command name, print help for that
3783 Given a topic, extension, or command name, print help for that
3784 topic.
3784 topic.
3785
3785
3786 Returns 0 if successful.
3786 Returns 0 if successful.
3787 """
3787 """
3788
3788
3789 keep = opts.get('system') or []
3789 keep = opts.get('system') or []
3790 if len(keep) == 0:
3790 if len(keep) == 0:
3791 if pycompat.sysplatform.startswith(b'win'):
3791 if pycompat.sysplatform.startswith(b'win'):
3792 keep.append(b'windows')
3792 keep.append(b'windows')
3793 elif pycompat.sysplatform == b'OpenVMS':
3793 elif pycompat.sysplatform == b'OpenVMS':
3794 keep.append(b'vms')
3794 keep.append(b'vms')
3795 elif pycompat.sysplatform == b'plan9':
3795 elif pycompat.sysplatform == b'plan9':
3796 keep.append(b'plan9')
3796 keep.append(b'plan9')
3797 else:
3797 else:
3798 keep.append(b'unix')
3798 keep.append(b'unix')
3799 keep.append(pycompat.sysplatform.lower())
3799 keep.append(pycompat.sysplatform.lower())
3800 if ui.verbose:
3800 if ui.verbose:
3801 keep.append(b'verbose')
3801 keep.append(b'verbose')
3802
3802
3803 commands = sys.modules[__name__]
3803 commands = sys.modules[__name__]
3804 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3804 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3805 ui.pager(b'help')
3805 ui.pager(b'help')
3806 ui.write(formatted)
3806 ui.write(formatted)
3807
3807
3808
3808
3809 @command(
3809 @command(
3810 b'identify|id',
3810 b'identify|id',
3811 [
3811 [
3812 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3812 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3813 (b'n', b'num', None, _(b'show local revision number')),
3813 (b'n', b'num', None, _(b'show local revision number')),
3814 (b'i', b'id', None, _(b'show global revision id')),
3814 (b'i', b'id', None, _(b'show global revision id')),
3815 (b'b', b'branch', None, _(b'show branch')),
3815 (b'b', b'branch', None, _(b'show branch')),
3816 (b't', b'tags', None, _(b'show tags')),
3816 (b't', b'tags', None, _(b'show tags')),
3817 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3817 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3818 ]
3818 ]
3819 + remoteopts
3819 + remoteopts
3820 + formatteropts,
3820 + formatteropts,
3821 _(b'[-nibtB] [-r REV] [SOURCE]'),
3821 _(b'[-nibtB] [-r REV] [SOURCE]'),
3822 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3822 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3823 optionalrepo=True,
3823 optionalrepo=True,
3824 intents={INTENT_READONLY},
3824 intents={INTENT_READONLY},
3825 )
3825 )
3826 def identify(
3826 def identify(
3827 ui,
3827 ui,
3828 repo,
3828 repo,
3829 source=None,
3829 source=None,
3830 rev=None,
3830 rev=None,
3831 num=None,
3831 num=None,
3832 id=None,
3832 id=None,
3833 branch=None,
3833 branch=None,
3834 tags=None,
3834 tags=None,
3835 bookmarks=None,
3835 bookmarks=None,
3836 **opts
3836 **opts
3837 ):
3837 ):
3838 """identify the working directory or specified revision
3838 """identify the working directory or specified revision
3839
3839
3840 Print a summary identifying the repository state at REV using one or
3840 Print a summary identifying the repository state at REV using one or
3841 two parent hash identifiers, followed by a "+" if the working
3841 two parent hash identifiers, followed by a "+" if the working
3842 directory has uncommitted changes, the branch name (if not default),
3842 directory has uncommitted changes, the branch name (if not default),
3843 a list of tags, and a list of bookmarks.
3843 a list of tags, and a list of bookmarks.
3844
3844
3845 When REV is not given, print a summary of the current state of the
3845 When REV is not given, print a summary of the current state of the
3846 repository including the working directory. Specify -r. to get information
3846 repository including the working directory. Specify -r. to get information
3847 of the working directory parent without scanning uncommitted changes.
3847 of the working directory parent without scanning uncommitted changes.
3848
3848
3849 Specifying a path to a repository root or Mercurial bundle will
3849 Specifying a path to a repository root or Mercurial bundle will
3850 cause lookup to operate on that repository/bundle.
3850 cause lookup to operate on that repository/bundle.
3851
3851
3852 .. container:: verbose
3852 .. container:: verbose
3853
3853
3854 Template:
3854 Template:
3855
3855
3856 The following keywords are supported in addition to the common template
3856 The following keywords are supported in addition to the common template
3857 keywords and functions. See also :hg:`help templates`.
3857 keywords and functions. See also :hg:`help templates`.
3858
3858
3859 :dirty: String. Character ``+`` denoting if the working directory has
3859 :dirty: String. Character ``+`` denoting if the working directory has
3860 uncommitted changes.
3860 uncommitted changes.
3861 :id: String. One or two nodes, optionally followed by ``+``.
3861 :id: String. One or two nodes, optionally followed by ``+``.
3862 :parents: List of strings. Parent nodes of the changeset.
3862 :parents: List of strings. Parent nodes of the changeset.
3863
3863
3864 Examples:
3864 Examples:
3865
3865
3866 - generate a build identifier for the working directory::
3866 - generate a build identifier for the working directory::
3867
3867
3868 hg id --id > build-id.dat
3868 hg id --id > build-id.dat
3869
3869
3870 - find the revision corresponding to a tag::
3870 - find the revision corresponding to a tag::
3871
3871
3872 hg id -n -r 1.3
3872 hg id -n -r 1.3
3873
3873
3874 - check the most recent revision of a remote repository::
3874 - check the most recent revision of a remote repository::
3875
3875
3876 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3876 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3877
3877
3878 See :hg:`log` for generating more information about specific revisions,
3878 See :hg:`log` for generating more information about specific revisions,
3879 including full hash identifiers.
3879 including full hash identifiers.
3880
3880
3881 Returns 0 if successful.
3881 Returns 0 if successful.
3882 """
3882 """
3883
3883
3884 opts = pycompat.byteskwargs(opts)
3884 opts = pycompat.byteskwargs(opts)
3885 if not repo and not source:
3885 if not repo and not source:
3886 raise error.Abort(
3886 raise error.Abort(
3887 _(b"there is no Mercurial repository here (.hg not found)")
3887 _(b"there is no Mercurial repository here (.hg not found)")
3888 )
3888 )
3889
3889
3890 default = not (num or id or branch or tags or bookmarks)
3890 default = not (num or id or branch or tags or bookmarks)
3891 output = []
3891 output = []
3892 revs = []
3892 revs = []
3893
3893
3894 if source:
3894 if source:
3895 source, branches = hg.parseurl(ui.expandpath(source))
3895 source, branches = hg.parseurl(ui.expandpath(source))
3896 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3896 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3897 repo = peer.local()
3897 repo = peer.local()
3898 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3898 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3899
3899
3900 fm = ui.formatter(b'identify', opts)
3900 fm = ui.formatter(b'identify', opts)
3901 fm.startitem()
3901 fm.startitem()
3902
3902
3903 if not repo:
3903 if not repo:
3904 if num or branch or tags:
3904 if num or branch or tags:
3905 raise error.Abort(
3905 raise error.Abort(
3906 _(b"can't query remote revision number, branch, or tags")
3906 _(b"can't query remote revision number, branch, or tags")
3907 )
3907 )
3908 if not rev and revs:
3908 if not rev and revs:
3909 rev = revs[0]
3909 rev = revs[0]
3910 if not rev:
3910 if not rev:
3911 rev = b"tip"
3911 rev = b"tip"
3912
3912
3913 remoterev = peer.lookup(rev)
3913 remoterev = peer.lookup(rev)
3914 hexrev = fm.hexfunc(remoterev)
3914 hexrev = fm.hexfunc(remoterev)
3915 if default or id:
3915 if default or id:
3916 output = [hexrev]
3916 output = [hexrev]
3917 fm.data(id=hexrev)
3917 fm.data(id=hexrev)
3918
3918
3919 @util.cachefunc
3919 @util.cachefunc
3920 def getbms():
3920 def getbms():
3921 bms = []
3921 bms = []
3922
3922
3923 if b'bookmarks' in peer.listkeys(b'namespaces'):
3923 if b'bookmarks' in peer.listkeys(b'namespaces'):
3924 hexremoterev = hex(remoterev)
3924 hexremoterev = hex(remoterev)
3925 bms = [
3925 bms = [
3926 bm
3926 bm
3927 for bm, bmr in pycompat.iteritems(
3927 for bm, bmr in pycompat.iteritems(
3928 peer.listkeys(b'bookmarks')
3928 peer.listkeys(b'bookmarks')
3929 )
3929 )
3930 if bmr == hexremoterev
3930 if bmr == hexremoterev
3931 ]
3931 ]
3932
3932
3933 return sorted(bms)
3933 return sorted(bms)
3934
3934
3935 if fm.isplain():
3935 if fm.isplain():
3936 if bookmarks:
3936 if bookmarks:
3937 output.extend(getbms())
3937 output.extend(getbms())
3938 elif default and not ui.quiet:
3938 elif default and not ui.quiet:
3939 # multiple bookmarks for a single parent separated by '/'
3939 # multiple bookmarks for a single parent separated by '/'
3940 bm = b'/'.join(getbms())
3940 bm = b'/'.join(getbms())
3941 if bm:
3941 if bm:
3942 output.append(bm)
3942 output.append(bm)
3943 else:
3943 else:
3944 fm.data(node=hex(remoterev))
3944 fm.data(node=hex(remoterev))
3945 if bookmarks or b'bookmarks' in fm.datahint():
3945 if bookmarks or b'bookmarks' in fm.datahint():
3946 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3946 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3947 else:
3947 else:
3948 if rev:
3948 if rev:
3949 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3949 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3950 ctx = scmutil.revsingle(repo, rev, None)
3950 ctx = scmutil.revsingle(repo, rev, None)
3951
3951
3952 if ctx.rev() is None:
3952 if ctx.rev() is None:
3953 ctx = repo[None]
3953 ctx = repo[None]
3954 parents = ctx.parents()
3954 parents = ctx.parents()
3955 taglist = []
3955 taglist = []
3956 for p in parents:
3956 for p in parents:
3957 taglist.extend(p.tags())
3957 taglist.extend(p.tags())
3958
3958
3959 dirty = b""
3959 dirty = b""
3960 if ctx.dirty(missing=True, merge=False, branch=False):
3960 if ctx.dirty(missing=True, merge=False, branch=False):
3961 dirty = b'+'
3961 dirty = b'+'
3962 fm.data(dirty=dirty)
3962 fm.data(dirty=dirty)
3963
3963
3964 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3964 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3965 if default or id:
3965 if default or id:
3966 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3966 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3967 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3967 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3968
3968
3969 if num:
3969 if num:
3970 numoutput = [b"%d" % p.rev() for p in parents]
3970 numoutput = [b"%d" % p.rev() for p in parents]
3971 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3971 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3972
3972
3973 fm.data(
3973 fm.data(
3974 parents=fm.formatlist(
3974 parents=fm.formatlist(
3975 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3975 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3976 )
3976 )
3977 )
3977 )
3978 else:
3978 else:
3979 hexoutput = fm.hexfunc(ctx.node())
3979 hexoutput = fm.hexfunc(ctx.node())
3980 if default or id:
3980 if default or id:
3981 output = [hexoutput]
3981 output = [hexoutput]
3982 fm.data(id=hexoutput)
3982 fm.data(id=hexoutput)
3983
3983
3984 if num:
3984 if num:
3985 output.append(pycompat.bytestr(ctx.rev()))
3985 output.append(pycompat.bytestr(ctx.rev()))
3986 taglist = ctx.tags()
3986 taglist = ctx.tags()
3987
3987
3988 if default and not ui.quiet:
3988 if default and not ui.quiet:
3989 b = ctx.branch()
3989 b = ctx.branch()
3990 if b != b'default':
3990 if b != b'default':
3991 output.append(b"(%s)" % b)
3991 output.append(b"(%s)" % b)
3992
3992
3993 # multiple tags for a single parent separated by '/'
3993 # multiple tags for a single parent separated by '/'
3994 t = b'/'.join(taglist)
3994 t = b'/'.join(taglist)
3995 if t:
3995 if t:
3996 output.append(t)
3996 output.append(t)
3997
3997
3998 # multiple bookmarks for a single parent separated by '/'
3998 # multiple bookmarks for a single parent separated by '/'
3999 bm = b'/'.join(ctx.bookmarks())
3999 bm = b'/'.join(ctx.bookmarks())
4000 if bm:
4000 if bm:
4001 output.append(bm)
4001 output.append(bm)
4002 else:
4002 else:
4003 if branch:
4003 if branch:
4004 output.append(ctx.branch())
4004 output.append(ctx.branch())
4005
4005
4006 if tags:
4006 if tags:
4007 output.extend(taglist)
4007 output.extend(taglist)
4008
4008
4009 if bookmarks:
4009 if bookmarks:
4010 output.extend(ctx.bookmarks())
4010 output.extend(ctx.bookmarks())
4011
4011
4012 fm.data(node=ctx.hex())
4012 fm.data(node=ctx.hex())
4013 fm.data(branch=ctx.branch())
4013 fm.data(branch=ctx.branch())
4014 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4014 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4015 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4015 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4016 fm.context(ctx=ctx)
4016 fm.context(ctx=ctx)
4017
4017
4018 fm.plain(b"%s\n" % b' '.join(output))
4018 fm.plain(b"%s\n" % b' '.join(output))
4019 fm.end()
4019 fm.end()
4020
4020
4021
4021
4022 @command(
4022 @command(
4023 b'import|patch',
4023 b'import|patch',
4024 [
4024 [
4025 (
4025 (
4026 b'p',
4026 b'p',
4027 b'strip',
4027 b'strip',
4028 1,
4028 1,
4029 _(
4029 _(
4030 b'directory strip option for patch. This has the same '
4030 b'directory strip option for patch. This has the same '
4031 b'meaning as the corresponding patch option'
4031 b'meaning as the corresponding patch option'
4032 ),
4032 ),
4033 _(b'NUM'),
4033 _(b'NUM'),
4034 ),
4034 ),
4035 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4035 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4036 (b'', b'secret', None, _(b'use the secret phase for committing')),
4036 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4037 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4037 (
4038 (
4038 b'f',
4039 b'f',
4039 b'force',
4040 b'force',
4040 None,
4041 None,
4041 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4042 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4042 ),
4043 ),
4043 (
4044 (
4044 b'',
4045 b'',
4045 b'no-commit',
4046 b'no-commit',
4046 None,
4047 None,
4047 _(b"don't commit, just update the working directory"),
4048 _(b"don't commit, just update the working directory"),
4048 ),
4049 ),
4049 (
4050 (
4050 b'',
4051 b'',
4051 b'bypass',
4052 b'bypass',
4052 None,
4053 None,
4053 _(b"apply patch without touching the working directory"),
4054 _(b"apply patch without touching the working directory"),
4054 ),
4055 ),
4055 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4056 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4056 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4057 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4057 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4058 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4058 (
4059 (
4059 b'',
4060 b'',
4060 b'import-branch',
4061 b'import-branch',
4061 None,
4062 None,
4062 _(b'use any branch information in patch (implied by --exact)'),
4063 _(b'use any branch information in patch (implied by --exact)'),
4063 ),
4064 ),
4064 ]
4065 ]
4065 + commitopts
4066 + commitopts
4066 + commitopts2
4067 + commitopts2
4067 + similarityopts,
4068 + similarityopts,
4068 _(b'[OPTION]... PATCH...'),
4069 _(b'[OPTION]... PATCH...'),
4069 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4070 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4070 )
4071 )
4071 def import_(ui, repo, patch1=None, *patches, **opts):
4072 def import_(ui, repo, patch1=None, *patches, **opts):
4072 """import an ordered set of patches
4073 """import an ordered set of patches
4073
4074
4074 Import a list of patches and commit them individually (unless
4075 Import a list of patches and commit them individually (unless
4075 --no-commit is specified).
4076 --no-commit is specified).
4076
4077
4077 To read a patch from standard input (stdin), use "-" as the patch
4078 To read a patch from standard input (stdin), use "-" as the patch
4078 name. If a URL is specified, the patch will be downloaded from
4079 name. If a URL is specified, the patch will be downloaded from
4079 there.
4080 there.
4080
4081
4081 Import first applies changes to the working directory (unless
4082 Import first applies changes to the working directory (unless
4082 --bypass is specified), import will abort if there are outstanding
4083 --bypass is specified), import will abort if there are outstanding
4083 changes.
4084 changes.
4084
4085
4085 Use --bypass to apply and commit patches directly to the
4086 Use --bypass to apply and commit patches directly to the
4086 repository, without affecting the working directory. Without
4087 repository, without affecting the working directory. Without
4087 --exact, patches will be applied on top of the working directory
4088 --exact, patches will be applied on top of the working directory
4088 parent revision.
4089 parent revision.
4089
4090
4090 You can import a patch straight from a mail message. Even patches
4091 You can import a patch straight from a mail message. Even patches
4091 as attachments work (to use the body part, it must have type
4092 as attachments work (to use the body part, it must have type
4092 text/plain or text/x-patch). From and Subject headers of email
4093 text/plain or text/x-patch). From and Subject headers of email
4093 message are used as default committer and commit message. All
4094 message are used as default committer and commit message. All
4094 text/plain body parts before first diff are added to the commit
4095 text/plain body parts before first diff are added to the commit
4095 message.
4096 message.
4096
4097
4097 If the imported patch was generated by :hg:`export`, user and
4098 If the imported patch was generated by :hg:`export`, user and
4098 description from patch override values from message headers and
4099 description from patch override values from message headers and
4099 body. Values given on command line with -m/--message and -u/--user
4100 body. Values given on command line with -m/--message and -u/--user
4100 override these.
4101 override these.
4101
4102
4102 If --exact is specified, import will set the working directory to
4103 If --exact is specified, import will set the working directory to
4103 the parent of each patch before applying it, and will abort if the
4104 the parent of each patch before applying it, and will abort if the
4104 resulting changeset has a different ID than the one recorded in
4105 resulting changeset has a different ID than the one recorded in
4105 the patch. This will guard against various ways that portable
4106 the patch. This will guard against various ways that portable
4106 patch formats and mail systems might fail to transfer Mercurial
4107 patch formats and mail systems might fail to transfer Mercurial
4107 data or metadata. See :hg:`bundle` for lossless transmission.
4108 data or metadata. See :hg:`bundle` for lossless transmission.
4108
4109
4109 Use --partial to ensure a changeset will be created from the patch
4110 Use --partial to ensure a changeset will be created from the patch
4110 even if some hunks fail to apply. Hunks that fail to apply will be
4111 even if some hunks fail to apply. Hunks that fail to apply will be
4111 written to a <target-file>.rej file. Conflicts can then be resolved
4112 written to a <target-file>.rej file. Conflicts can then be resolved
4112 by hand before :hg:`commit --amend` is run to update the created
4113 by hand before :hg:`commit --amend` is run to update the created
4113 changeset. This flag exists to let people import patches that
4114 changeset. This flag exists to let people import patches that
4114 partially apply without losing the associated metadata (author,
4115 partially apply without losing the associated metadata (author,
4115 date, description, ...).
4116 date, description, ...).
4116
4117
4117 .. note::
4118 .. note::
4118
4119
4119 When no hunks apply cleanly, :hg:`import --partial` will create
4120 When no hunks apply cleanly, :hg:`import --partial` will create
4120 an empty changeset, importing only the patch metadata.
4121 an empty changeset, importing only the patch metadata.
4121
4122
4122 With -s/--similarity, hg will attempt to discover renames and
4123 With -s/--similarity, hg will attempt to discover renames and
4123 copies in the patch in the same way as :hg:`addremove`.
4124 copies in the patch in the same way as :hg:`addremove`.
4124
4125
4125 It is possible to use external patch programs to perform the patch
4126 It is possible to use external patch programs to perform the patch
4126 by setting the ``ui.patch`` configuration option. For the default
4127 by setting the ``ui.patch`` configuration option. For the default
4127 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4128 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4128 See :hg:`help config` for more information about configuration
4129 See :hg:`help config` for more information about configuration
4129 files and how to use these options.
4130 files and how to use these options.
4130
4131
4131 See :hg:`help dates` for a list of formats valid for -d/--date.
4132 See :hg:`help dates` for a list of formats valid for -d/--date.
4132
4133
4133 .. container:: verbose
4134 .. container:: verbose
4134
4135
4135 Examples:
4136 Examples:
4136
4137
4137 - import a traditional patch from a website and detect renames::
4138 - import a traditional patch from a website and detect renames::
4138
4139
4139 hg import -s 80 http://example.com/bugfix.patch
4140 hg import -s 80 http://example.com/bugfix.patch
4140
4141
4141 - import a changeset from an hgweb server::
4142 - import a changeset from an hgweb server::
4142
4143
4143 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4144 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4144
4145
4145 - import all the patches in an Unix-style mbox::
4146 - import all the patches in an Unix-style mbox::
4146
4147
4147 hg import incoming-patches.mbox
4148 hg import incoming-patches.mbox
4148
4149
4149 - import patches from stdin::
4150 - import patches from stdin::
4150
4151
4151 hg import -
4152 hg import -
4152
4153
4153 - attempt to exactly restore an exported changeset (not always
4154 - attempt to exactly restore an exported changeset (not always
4154 possible)::
4155 possible)::
4155
4156
4156 hg import --exact proposed-fix.patch
4157 hg import --exact proposed-fix.patch
4157
4158
4158 - use an external tool to apply a patch which is too fuzzy for
4159 - use an external tool to apply a patch which is too fuzzy for
4159 the default internal tool.
4160 the default internal tool.
4160
4161
4161 hg import --config ui.patch="patch --merge" fuzzy.patch
4162 hg import --config ui.patch="patch --merge" fuzzy.patch
4162
4163
4163 - change the default fuzzing from 2 to a less strict 7
4164 - change the default fuzzing from 2 to a less strict 7
4164
4165
4165 hg import --config ui.fuzz=7 fuzz.patch
4166 hg import --config ui.fuzz=7 fuzz.patch
4166
4167
4167 Returns 0 on success, 1 on partial success (see --partial).
4168 Returns 0 on success, 1 on partial success (see --partial).
4168 """
4169 """
4169
4170
4170 opts = pycompat.byteskwargs(opts)
4171 opts = pycompat.byteskwargs(opts)
4171 if not patch1:
4172 if not patch1:
4172 raise error.Abort(_(b'need at least one patch to import'))
4173 raise error.Abort(_(b'need at least one patch to import'))
4173
4174
4174 patches = (patch1,) + patches
4175 patches = (patch1,) + patches
4175
4176
4176 date = opts.get(b'date')
4177 date = opts.get(b'date')
4177 if date:
4178 if date:
4178 opts[b'date'] = dateutil.parsedate(date)
4179 opts[b'date'] = dateutil.parsedate(date)
4179
4180
4180 exact = opts.get(b'exact')
4181 exact = opts.get(b'exact')
4181 update = not opts.get(b'bypass')
4182 update = not opts.get(b'bypass')
4182 if not update and opts.get(b'no_commit'):
4183 if not update and opts.get(b'no_commit'):
4183 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4184 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4185 if opts.get(b'secret') and opts.get(b'no_commit'):
4186 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4184 try:
4187 try:
4185 sim = float(opts.get(b'similarity') or 0)
4188 sim = float(opts.get(b'similarity') or 0)
4186 except ValueError:
4189 except ValueError:
4187 raise error.Abort(_(b'similarity must be a number'))
4190 raise error.Abort(_(b'similarity must be a number'))
4188 if sim < 0 or sim > 100:
4191 if sim < 0 or sim > 100:
4189 raise error.Abort(_(b'similarity must be between 0 and 100'))
4192 raise error.Abort(_(b'similarity must be between 0 and 100'))
4190 if sim and not update:
4193 if sim and not update:
4191 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4194 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4192 if exact:
4195 if exact:
4193 if opts.get(b'edit'):
4196 if opts.get(b'edit'):
4194 raise error.Abort(_(b'cannot use --exact with --edit'))
4197 raise error.Abort(_(b'cannot use --exact with --edit'))
4195 if opts.get(b'prefix'):
4198 if opts.get(b'prefix'):
4196 raise error.Abort(_(b'cannot use --exact with --prefix'))
4199 raise error.Abort(_(b'cannot use --exact with --prefix'))
4197
4200
4198 base = opts[b"base"]
4201 base = opts[b"base"]
4199 msgs = []
4202 msgs = []
4200 ret = 0
4203 ret = 0
4201
4204
4202 with repo.wlock():
4205 with repo.wlock():
4203 if update:
4206 if update:
4204 cmdutil.checkunfinished(repo)
4207 cmdutil.checkunfinished(repo)
4205 if exact or not opts.get(b'force'):
4208 if exact or not opts.get(b'force'):
4206 cmdutil.bailifchanged(repo)
4209 cmdutil.bailifchanged(repo)
4207
4210
4208 if not opts.get(b'no_commit'):
4211 if not opts.get(b'no_commit'):
4209 lock = repo.lock
4212 lock = repo.lock
4210 tr = lambda: repo.transaction(b'import')
4213 tr = lambda: repo.transaction(b'import')
4211 dsguard = util.nullcontextmanager
4214 dsguard = util.nullcontextmanager
4212 else:
4215 else:
4213 lock = util.nullcontextmanager
4216 lock = util.nullcontextmanager
4214 tr = util.nullcontextmanager
4217 tr = util.nullcontextmanager
4215 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4218 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4216 with lock(), tr(), dsguard():
4219 with lock(), tr(), dsguard():
4217 parents = repo[None].parents()
4220 parents = repo[None].parents()
4218 for patchurl in patches:
4221 for patchurl in patches:
4219 if patchurl == b'-':
4222 if patchurl == b'-':
4220 ui.status(_(b'applying patch from stdin\n'))
4223 ui.status(_(b'applying patch from stdin\n'))
4221 patchfile = ui.fin
4224 patchfile = ui.fin
4222 patchurl = b'stdin' # for error message
4225 patchurl = b'stdin' # for error message
4223 else:
4226 else:
4224 patchurl = os.path.join(base, patchurl)
4227 patchurl = os.path.join(base, patchurl)
4225 ui.status(_(b'applying %s\n') % patchurl)
4228 ui.status(_(b'applying %s\n') % patchurl)
4226 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4229 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4227
4230
4228 haspatch = False
4231 haspatch = False
4229 for hunk in patch.split(patchfile):
4232 for hunk in patch.split(patchfile):
4230 with patch.extract(ui, hunk) as patchdata:
4233 with patch.extract(ui, hunk) as patchdata:
4231 msg, node, rej = cmdutil.tryimportone(
4234 msg, node, rej = cmdutil.tryimportone(
4232 ui, repo, patchdata, parents, opts, msgs, hg.clean
4235 ui, repo, patchdata, parents, opts, msgs, hg.clean
4233 )
4236 )
4234 if msg:
4237 if msg:
4235 haspatch = True
4238 haspatch = True
4236 ui.note(msg + b'\n')
4239 ui.note(msg + b'\n')
4237 if update or exact:
4240 if update or exact:
4238 parents = repo[None].parents()
4241 parents = repo[None].parents()
4239 else:
4242 else:
4240 parents = [repo[node]]
4243 parents = [repo[node]]
4241 if rej:
4244 if rej:
4242 ui.write_err(_(b"patch applied partially\n"))
4245 ui.write_err(_(b"patch applied partially\n"))
4243 ui.write_err(
4246 ui.write_err(
4244 _(
4247 _(
4245 b"(fix the .rej files and run "
4248 b"(fix the .rej files and run "
4246 b"`hg commit --amend`)\n"
4249 b"`hg commit --amend`)\n"
4247 )
4250 )
4248 )
4251 )
4249 ret = 1
4252 ret = 1
4250 break
4253 break
4251
4254
4252 if not haspatch:
4255 if not haspatch:
4253 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4256 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4254
4257
4255 if msgs:
4258 if msgs:
4256 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4259 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4257 return ret
4260 return ret
4258
4261
4259
4262
4260 @command(
4263 @command(
4261 b'incoming|in',
4264 b'incoming|in',
4262 [
4265 [
4263 (
4266 (
4264 b'f',
4267 b'f',
4265 b'force',
4268 b'force',
4266 None,
4269 None,
4267 _(b'run even if remote repository is unrelated'),
4270 _(b'run even if remote repository is unrelated'),
4268 ),
4271 ),
4269 (b'n', b'newest-first', None, _(b'show newest record first')),
4272 (b'n', b'newest-first', None, _(b'show newest record first')),
4270 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4273 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4271 (
4274 (
4272 b'r',
4275 b'r',
4273 b'rev',
4276 b'rev',
4274 [],
4277 [],
4275 _(b'a remote changeset intended to be added'),
4278 _(b'a remote changeset intended to be added'),
4276 _(b'REV'),
4279 _(b'REV'),
4277 ),
4280 ),
4278 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4281 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4279 (
4282 (
4280 b'b',
4283 b'b',
4281 b'branch',
4284 b'branch',
4282 [],
4285 [],
4283 _(b'a specific branch you would like to pull'),
4286 _(b'a specific branch you would like to pull'),
4284 _(b'BRANCH'),
4287 _(b'BRANCH'),
4285 ),
4288 ),
4286 ]
4289 ]
4287 + logopts
4290 + logopts
4288 + remoteopts
4291 + remoteopts
4289 + subrepoopts,
4292 + subrepoopts,
4290 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4293 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4291 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4294 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4292 )
4295 )
4293 def incoming(ui, repo, source=b"default", **opts):
4296 def incoming(ui, repo, source=b"default", **opts):
4294 """show new changesets found in source
4297 """show new changesets found in source
4295
4298
4296 Show new changesets found in the specified path/URL or the default
4299 Show new changesets found in the specified path/URL or the default
4297 pull location. These are the changesets that would have been pulled
4300 pull location. These are the changesets that would have been pulled
4298 by :hg:`pull` at the time you issued this command.
4301 by :hg:`pull` at the time you issued this command.
4299
4302
4300 See pull for valid source format details.
4303 See pull for valid source format details.
4301
4304
4302 .. container:: verbose
4305 .. container:: verbose
4303
4306
4304 With -B/--bookmarks, the result of bookmark comparison between
4307 With -B/--bookmarks, the result of bookmark comparison between
4305 local and remote repositories is displayed. With -v/--verbose,
4308 local and remote repositories is displayed. With -v/--verbose,
4306 status is also displayed for each bookmark like below::
4309 status is also displayed for each bookmark like below::
4307
4310
4308 BM1 01234567890a added
4311 BM1 01234567890a added
4309 BM2 1234567890ab advanced
4312 BM2 1234567890ab advanced
4310 BM3 234567890abc diverged
4313 BM3 234567890abc diverged
4311 BM4 34567890abcd changed
4314 BM4 34567890abcd changed
4312
4315
4313 The action taken locally when pulling depends on the
4316 The action taken locally when pulling depends on the
4314 status of each bookmark:
4317 status of each bookmark:
4315
4318
4316 :``added``: pull will create it
4319 :``added``: pull will create it
4317 :``advanced``: pull will update it
4320 :``advanced``: pull will update it
4318 :``diverged``: pull will create a divergent bookmark
4321 :``diverged``: pull will create a divergent bookmark
4319 :``changed``: result depends on remote changesets
4322 :``changed``: result depends on remote changesets
4320
4323
4321 From the point of view of pulling behavior, bookmark
4324 From the point of view of pulling behavior, bookmark
4322 existing only in the remote repository are treated as ``added``,
4325 existing only in the remote repository are treated as ``added``,
4323 even if it is in fact locally deleted.
4326 even if it is in fact locally deleted.
4324
4327
4325 .. container:: verbose
4328 .. container:: verbose
4326
4329
4327 For remote repository, using --bundle avoids downloading the
4330 For remote repository, using --bundle avoids downloading the
4328 changesets twice if the incoming is followed by a pull.
4331 changesets twice if the incoming is followed by a pull.
4329
4332
4330 Examples:
4333 Examples:
4331
4334
4332 - show incoming changes with patches and full description::
4335 - show incoming changes with patches and full description::
4333
4336
4334 hg incoming -vp
4337 hg incoming -vp
4335
4338
4336 - show incoming changes excluding merges, store a bundle::
4339 - show incoming changes excluding merges, store a bundle::
4337
4340
4338 hg in -vpM --bundle incoming.hg
4341 hg in -vpM --bundle incoming.hg
4339 hg pull incoming.hg
4342 hg pull incoming.hg
4340
4343
4341 - briefly list changes inside a bundle::
4344 - briefly list changes inside a bundle::
4342
4345
4343 hg in changes.hg -T "{desc|firstline}\\n"
4346 hg in changes.hg -T "{desc|firstline}\\n"
4344
4347
4345 Returns 0 if there are incoming changes, 1 otherwise.
4348 Returns 0 if there are incoming changes, 1 otherwise.
4346 """
4349 """
4347 opts = pycompat.byteskwargs(opts)
4350 opts = pycompat.byteskwargs(opts)
4348 if opts.get(b'graph'):
4351 if opts.get(b'graph'):
4349 logcmdutil.checkunsupportedgraphflags([], opts)
4352 logcmdutil.checkunsupportedgraphflags([], opts)
4350
4353
4351 def display(other, chlist, displayer):
4354 def display(other, chlist, displayer):
4352 revdag = logcmdutil.graphrevs(other, chlist, opts)
4355 revdag = logcmdutil.graphrevs(other, chlist, opts)
4353 logcmdutil.displaygraph(
4356 logcmdutil.displaygraph(
4354 ui, repo, revdag, displayer, graphmod.asciiedges
4357 ui, repo, revdag, displayer, graphmod.asciiedges
4355 )
4358 )
4356
4359
4357 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4360 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4358 return 0
4361 return 0
4359
4362
4360 if opts.get(b'bundle') and opts.get(b'subrepos'):
4363 if opts.get(b'bundle') and opts.get(b'subrepos'):
4361 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4364 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4362
4365
4363 if opts.get(b'bookmarks'):
4366 if opts.get(b'bookmarks'):
4364 source, branches = hg.parseurl(
4367 source, branches = hg.parseurl(
4365 ui.expandpath(source), opts.get(b'branch')
4368 ui.expandpath(source), opts.get(b'branch')
4366 )
4369 )
4367 other = hg.peer(repo, opts, source)
4370 other = hg.peer(repo, opts, source)
4368 if b'bookmarks' not in other.listkeys(b'namespaces'):
4371 if b'bookmarks' not in other.listkeys(b'namespaces'):
4369 ui.warn(_(b"remote doesn't support bookmarks\n"))
4372 ui.warn(_(b"remote doesn't support bookmarks\n"))
4370 return 0
4373 return 0
4371 ui.pager(b'incoming')
4374 ui.pager(b'incoming')
4372 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4375 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4373 return bookmarks.incoming(ui, repo, other)
4376 return bookmarks.incoming(ui, repo, other)
4374
4377
4375 repo._subtoppath = ui.expandpath(source)
4378 repo._subtoppath = ui.expandpath(source)
4376 try:
4379 try:
4377 return hg.incoming(ui, repo, source, opts)
4380 return hg.incoming(ui, repo, source, opts)
4378 finally:
4381 finally:
4379 del repo._subtoppath
4382 del repo._subtoppath
4380
4383
4381
4384
4382 @command(
4385 @command(
4383 b'init',
4386 b'init',
4384 remoteopts,
4387 remoteopts,
4385 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4388 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4386 helpcategory=command.CATEGORY_REPO_CREATION,
4389 helpcategory=command.CATEGORY_REPO_CREATION,
4387 helpbasic=True,
4390 helpbasic=True,
4388 norepo=True,
4391 norepo=True,
4389 )
4392 )
4390 def init(ui, dest=b".", **opts):
4393 def init(ui, dest=b".", **opts):
4391 """create a new repository in the given directory
4394 """create a new repository in the given directory
4392
4395
4393 Initialize a new repository in the given directory. If the given
4396 Initialize a new repository in the given directory. If the given
4394 directory does not exist, it will be created.
4397 directory does not exist, it will be created.
4395
4398
4396 If no directory is given, the current directory is used.
4399 If no directory is given, the current directory is used.
4397
4400
4398 It is possible to specify an ``ssh://`` URL as the destination.
4401 It is possible to specify an ``ssh://`` URL as the destination.
4399 See :hg:`help urls` for more information.
4402 See :hg:`help urls` for more information.
4400
4403
4401 Returns 0 on success.
4404 Returns 0 on success.
4402 """
4405 """
4403 opts = pycompat.byteskwargs(opts)
4406 opts = pycompat.byteskwargs(opts)
4404 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4407 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4405
4408
4406
4409
4407 @command(
4410 @command(
4408 b'locate',
4411 b'locate',
4409 [
4412 [
4410 (
4413 (
4411 b'r',
4414 b'r',
4412 b'rev',
4415 b'rev',
4413 b'',
4416 b'',
4414 _(b'search the repository as it is in REV'),
4417 _(b'search the repository as it is in REV'),
4415 _(b'REV'),
4418 _(b'REV'),
4416 ),
4419 ),
4417 (
4420 (
4418 b'0',
4421 b'0',
4419 b'print0',
4422 b'print0',
4420 None,
4423 None,
4421 _(b'end filenames with NUL, for use with xargs'),
4424 _(b'end filenames with NUL, for use with xargs'),
4422 ),
4425 ),
4423 (
4426 (
4424 b'f',
4427 b'f',
4425 b'fullpath',
4428 b'fullpath',
4426 None,
4429 None,
4427 _(b'print complete paths from the filesystem root'),
4430 _(b'print complete paths from the filesystem root'),
4428 ),
4431 ),
4429 ]
4432 ]
4430 + walkopts,
4433 + walkopts,
4431 _(b'[OPTION]... [PATTERN]...'),
4434 _(b'[OPTION]... [PATTERN]...'),
4432 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4435 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4433 )
4436 )
4434 def locate(ui, repo, *pats, **opts):
4437 def locate(ui, repo, *pats, **opts):
4435 """locate files matching specific patterns (DEPRECATED)
4438 """locate files matching specific patterns (DEPRECATED)
4436
4439
4437 Print files under Mercurial control in the working directory whose
4440 Print files under Mercurial control in the working directory whose
4438 names match the given patterns.
4441 names match the given patterns.
4439
4442
4440 By default, this command searches all directories in the working
4443 By default, this command searches all directories in the working
4441 directory. To search just the current directory and its
4444 directory. To search just the current directory and its
4442 subdirectories, use "--include .".
4445 subdirectories, use "--include .".
4443
4446
4444 If no patterns are given to match, this command prints the names
4447 If no patterns are given to match, this command prints the names
4445 of all files under Mercurial control in the working directory.
4448 of all files under Mercurial control in the working directory.
4446
4449
4447 If you want to feed the output of this command into the "xargs"
4450 If you want to feed the output of this command into the "xargs"
4448 command, use the -0 option to both this command and "xargs". This
4451 command, use the -0 option to both this command and "xargs". This
4449 will avoid the problem of "xargs" treating single filenames that
4452 will avoid the problem of "xargs" treating single filenames that
4450 contain whitespace as multiple filenames.
4453 contain whitespace as multiple filenames.
4451
4454
4452 See :hg:`help files` for a more versatile command.
4455 See :hg:`help files` for a more versatile command.
4453
4456
4454 Returns 0 if a match is found, 1 otherwise.
4457 Returns 0 if a match is found, 1 otherwise.
4455 """
4458 """
4456 opts = pycompat.byteskwargs(opts)
4459 opts = pycompat.byteskwargs(opts)
4457 if opts.get(b'print0'):
4460 if opts.get(b'print0'):
4458 end = b'\0'
4461 end = b'\0'
4459 else:
4462 else:
4460 end = b'\n'
4463 end = b'\n'
4461 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4464 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4462
4465
4463 ret = 1
4466 ret = 1
4464 m = scmutil.match(
4467 m = scmutil.match(
4465 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4468 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4466 )
4469 )
4467
4470
4468 ui.pager(b'locate')
4471 ui.pager(b'locate')
4469 if ctx.rev() is None:
4472 if ctx.rev() is None:
4470 # When run on the working copy, "locate" includes removed files, so
4473 # When run on the working copy, "locate" includes removed files, so
4471 # we get the list of files from the dirstate.
4474 # we get the list of files from the dirstate.
4472 filesgen = sorted(repo.dirstate.matches(m))
4475 filesgen = sorted(repo.dirstate.matches(m))
4473 else:
4476 else:
4474 filesgen = ctx.matches(m)
4477 filesgen = ctx.matches(m)
4475 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4478 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4476 for abs in filesgen:
4479 for abs in filesgen:
4477 if opts.get(b'fullpath'):
4480 if opts.get(b'fullpath'):
4478 ui.write(repo.wjoin(abs), end)
4481 ui.write(repo.wjoin(abs), end)
4479 else:
4482 else:
4480 ui.write(uipathfn(abs), end)
4483 ui.write(uipathfn(abs), end)
4481 ret = 0
4484 ret = 0
4482
4485
4483 return ret
4486 return ret
4484
4487
4485
4488
4486 @command(
4489 @command(
4487 b'log|history',
4490 b'log|history',
4488 [
4491 [
4489 (
4492 (
4490 b'f',
4493 b'f',
4491 b'follow',
4494 b'follow',
4492 None,
4495 None,
4493 _(
4496 _(
4494 b'follow changeset history, or file history across copies and renames'
4497 b'follow changeset history, or file history across copies and renames'
4495 ),
4498 ),
4496 ),
4499 ),
4497 (
4500 (
4498 b'',
4501 b'',
4499 b'follow-first',
4502 b'follow-first',
4500 None,
4503 None,
4501 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4504 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4502 ),
4505 ),
4503 (
4506 (
4504 b'd',
4507 b'd',
4505 b'date',
4508 b'date',
4506 b'',
4509 b'',
4507 _(b'show revisions matching date spec'),
4510 _(b'show revisions matching date spec'),
4508 _(b'DATE'),
4511 _(b'DATE'),
4509 ),
4512 ),
4510 (b'C', b'copies', None, _(b'show copied files')),
4513 (b'C', b'copies', None, _(b'show copied files')),
4511 (
4514 (
4512 b'k',
4515 b'k',
4513 b'keyword',
4516 b'keyword',
4514 [],
4517 [],
4515 _(b'do case-insensitive search for a given text'),
4518 _(b'do case-insensitive search for a given text'),
4516 _(b'TEXT'),
4519 _(b'TEXT'),
4517 ),
4520 ),
4518 (
4521 (
4519 b'r',
4522 b'r',
4520 b'rev',
4523 b'rev',
4521 [],
4524 [],
4522 _(b'show the specified revision or revset'),
4525 _(b'show the specified revision or revset'),
4523 _(b'REV'),
4526 _(b'REV'),
4524 ),
4527 ),
4525 (
4528 (
4526 b'L',
4529 b'L',
4527 b'line-range',
4530 b'line-range',
4528 [],
4531 [],
4529 _(b'follow line range of specified file (EXPERIMENTAL)'),
4532 _(b'follow line range of specified file (EXPERIMENTAL)'),
4530 _(b'FILE,RANGE'),
4533 _(b'FILE,RANGE'),
4531 ),
4534 ),
4532 (
4535 (
4533 b'',
4536 b'',
4534 b'removed',
4537 b'removed',
4535 None,
4538 None,
4536 _(b'include revisions where files were removed'),
4539 _(b'include revisions where files were removed'),
4537 ),
4540 ),
4538 (
4541 (
4539 b'm',
4542 b'm',
4540 b'only-merges',
4543 b'only-merges',
4541 None,
4544 None,
4542 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4545 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4543 ),
4546 ),
4544 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4547 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4545 (
4548 (
4546 b'',
4549 b'',
4547 b'only-branch',
4550 b'only-branch',
4548 [],
4551 [],
4549 _(
4552 _(
4550 b'show only changesets within the given named branch (DEPRECATED)'
4553 b'show only changesets within the given named branch (DEPRECATED)'
4551 ),
4554 ),
4552 _(b'BRANCH'),
4555 _(b'BRANCH'),
4553 ),
4556 ),
4554 (
4557 (
4555 b'b',
4558 b'b',
4556 b'branch',
4559 b'branch',
4557 [],
4560 [],
4558 _(b'show changesets within the given named branch'),
4561 _(b'show changesets within the given named branch'),
4559 _(b'BRANCH'),
4562 _(b'BRANCH'),
4560 ),
4563 ),
4561 (
4564 (
4562 b'P',
4565 b'P',
4563 b'prune',
4566 b'prune',
4564 [],
4567 [],
4565 _(b'do not display revision or any of its ancestors'),
4568 _(b'do not display revision or any of its ancestors'),
4566 _(b'REV'),
4569 _(b'REV'),
4567 ),
4570 ),
4568 ]
4571 ]
4569 + logopts
4572 + logopts
4570 + walkopts,
4573 + walkopts,
4571 _(b'[OPTION]... [FILE]'),
4574 _(b'[OPTION]... [FILE]'),
4572 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4575 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4573 helpbasic=True,
4576 helpbasic=True,
4574 inferrepo=True,
4577 inferrepo=True,
4575 intents={INTENT_READONLY},
4578 intents={INTENT_READONLY},
4576 )
4579 )
4577 def log(ui, repo, *pats, **opts):
4580 def log(ui, repo, *pats, **opts):
4578 """show revision history of entire repository or files
4581 """show revision history of entire repository or files
4579
4582
4580 Print the revision history of the specified files or the entire
4583 Print the revision history of the specified files or the entire
4581 project.
4584 project.
4582
4585
4583 If no revision range is specified, the default is ``tip:0`` unless
4586 If no revision range is specified, the default is ``tip:0`` unless
4584 --follow is set, in which case the working directory parent is
4587 --follow is set, in which case the working directory parent is
4585 used as the starting revision.
4588 used as the starting revision.
4586
4589
4587 File history is shown without following rename or copy history of
4590 File history is shown without following rename or copy history of
4588 files. Use -f/--follow with a filename to follow history across
4591 files. Use -f/--follow with a filename to follow history across
4589 renames and copies. --follow without a filename will only show
4592 renames and copies. --follow without a filename will only show
4590 ancestors of the starting revision.
4593 ancestors of the starting revision.
4591
4594
4592 By default this command prints revision number and changeset id,
4595 By default this command prints revision number and changeset id,
4593 tags, non-trivial parents, user, date and time, and a summary for
4596 tags, non-trivial parents, user, date and time, and a summary for
4594 each commit. When the -v/--verbose switch is used, the list of
4597 each commit. When the -v/--verbose switch is used, the list of
4595 changed files and full commit message are shown.
4598 changed files and full commit message are shown.
4596
4599
4597 With --graph the revisions are shown as an ASCII art DAG with the most
4600 With --graph the revisions are shown as an ASCII art DAG with the most
4598 recent changeset at the top.
4601 recent changeset at the top.
4599 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4602 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4600 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4603 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4601 changeset from the lines below is a parent of the 'o' merge on the same
4604 changeset from the lines below is a parent of the 'o' merge on the same
4602 line.
4605 line.
4603 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4606 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4604 of a '|' indicates one or more revisions in a path are omitted.
4607 of a '|' indicates one or more revisions in a path are omitted.
4605
4608
4606 .. container:: verbose
4609 .. container:: verbose
4607
4610
4608 Use -L/--line-range FILE,M:N options to follow the history of lines
4611 Use -L/--line-range FILE,M:N options to follow the history of lines
4609 from M to N in FILE. With -p/--patch only diff hunks affecting
4612 from M to N in FILE. With -p/--patch only diff hunks affecting
4610 specified line range will be shown. This option requires --follow;
4613 specified line range will be shown. This option requires --follow;
4611 it can be specified multiple times. Currently, this option is not
4614 it can be specified multiple times. Currently, this option is not
4612 compatible with --graph. This option is experimental.
4615 compatible with --graph. This option is experimental.
4613
4616
4614 .. note::
4617 .. note::
4615
4618
4616 :hg:`log --patch` may generate unexpected diff output for merge
4619 :hg:`log --patch` may generate unexpected diff output for merge
4617 changesets, as it will only compare the merge changeset against
4620 changesets, as it will only compare the merge changeset against
4618 its first parent. Also, only files different from BOTH parents
4621 its first parent. Also, only files different from BOTH parents
4619 will appear in files:.
4622 will appear in files:.
4620
4623
4621 .. note::
4624 .. note::
4622
4625
4623 For performance reasons, :hg:`log FILE` may omit duplicate changes
4626 For performance reasons, :hg:`log FILE` may omit duplicate changes
4624 made on branches and will not show removals or mode changes. To
4627 made on branches and will not show removals or mode changes. To
4625 see all such changes, use the --removed switch.
4628 see all such changes, use the --removed switch.
4626
4629
4627 .. container:: verbose
4630 .. container:: verbose
4628
4631
4629 .. note::
4632 .. note::
4630
4633
4631 The history resulting from -L/--line-range options depends on diff
4634 The history resulting from -L/--line-range options depends on diff
4632 options; for instance if white-spaces are ignored, respective changes
4635 options; for instance if white-spaces are ignored, respective changes
4633 with only white-spaces in specified line range will not be listed.
4636 with only white-spaces in specified line range will not be listed.
4634
4637
4635 .. container:: verbose
4638 .. container:: verbose
4636
4639
4637 Some examples:
4640 Some examples:
4638
4641
4639 - changesets with full descriptions and file lists::
4642 - changesets with full descriptions and file lists::
4640
4643
4641 hg log -v
4644 hg log -v
4642
4645
4643 - changesets ancestral to the working directory::
4646 - changesets ancestral to the working directory::
4644
4647
4645 hg log -f
4648 hg log -f
4646
4649
4647 - last 10 commits on the current branch::
4650 - last 10 commits on the current branch::
4648
4651
4649 hg log -l 10 -b .
4652 hg log -l 10 -b .
4650
4653
4651 - changesets showing all modifications of a file, including removals::
4654 - changesets showing all modifications of a file, including removals::
4652
4655
4653 hg log --removed file.c
4656 hg log --removed file.c
4654
4657
4655 - all changesets that touch a directory, with diffs, excluding merges::
4658 - all changesets that touch a directory, with diffs, excluding merges::
4656
4659
4657 hg log -Mp lib/
4660 hg log -Mp lib/
4658
4661
4659 - all revision numbers that match a keyword::
4662 - all revision numbers that match a keyword::
4660
4663
4661 hg log -k bug --template "{rev}\\n"
4664 hg log -k bug --template "{rev}\\n"
4662
4665
4663 - the full hash identifier of the working directory parent::
4666 - the full hash identifier of the working directory parent::
4664
4667
4665 hg log -r . --template "{node}\\n"
4668 hg log -r . --template "{node}\\n"
4666
4669
4667 - list available log templates::
4670 - list available log templates::
4668
4671
4669 hg log -T list
4672 hg log -T list
4670
4673
4671 - check if a given changeset is included in a tagged release::
4674 - check if a given changeset is included in a tagged release::
4672
4675
4673 hg log -r "a21ccf and ancestor(1.9)"
4676 hg log -r "a21ccf and ancestor(1.9)"
4674
4677
4675 - find all changesets by some user in a date range::
4678 - find all changesets by some user in a date range::
4676
4679
4677 hg log -k alice -d "may 2008 to jul 2008"
4680 hg log -k alice -d "may 2008 to jul 2008"
4678
4681
4679 - summary of all changesets after the last tag::
4682 - summary of all changesets after the last tag::
4680
4683
4681 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4684 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4682
4685
4683 - changesets touching lines 13 to 23 for file.c::
4686 - changesets touching lines 13 to 23 for file.c::
4684
4687
4685 hg log -L file.c,13:23
4688 hg log -L file.c,13:23
4686
4689
4687 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4690 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4688 main.c with patch::
4691 main.c with patch::
4689
4692
4690 hg log -L file.c,13:23 -L main.c,2:6 -p
4693 hg log -L file.c,13:23 -L main.c,2:6 -p
4691
4694
4692 See :hg:`help dates` for a list of formats valid for -d/--date.
4695 See :hg:`help dates` for a list of formats valid for -d/--date.
4693
4696
4694 See :hg:`help revisions` for more about specifying and ordering
4697 See :hg:`help revisions` for more about specifying and ordering
4695 revisions.
4698 revisions.
4696
4699
4697 See :hg:`help templates` for more about pre-packaged styles and
4700 See :hg:`help templates` for more about pre-packaged styles and
4698 specifying custom templates. The default template used by the log
4701 specifying custom templates. The default template used by the log
4699 command can be customized via the ``ui.logtemplate`` configuration
4702 command can be customized via the ``ui.logtemplate`` configuration
4700 setting.
4703 setting.
4701
4704
4702 Returns 0 on success.
4705 Returns 0 on success.
4703
4706
4704 """
4707 """
4705 opts = pycompat.byteskwargs(opts)
4708 opts = pycompat.byteskwargs(opts)
4706 linerange = opts.get(b'line_range')
4709 linerange = opts.get(b'line_range')
4707
4710
4708 if linerange and not opts.get(b'follow'):
4711 if linerange and not opts.get(b'follow'):
4709 raise error.Abort(_(b'--line-range requires --follow'))
4712 raise error.Abort(_(b'--line-range requires --follow'))
4710
4713
4711 if linerange and pats:
4714 if linerange and pats:
4712 # TODO: take pats as patterns with no line-range filter
4715 # TODO: take pats as patterns with no line-range filter
4713 raise error.Abort(
4716 raise error.Abort(
4714 _(b'FILE arguments are not compatible with --line-range option')
4717 _(b'FILE arguments are not compatible with --line-range option')
4715 )
4718 )
4716
4719
4717 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4720 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4718 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4721 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4719 if linerange:
4722 if linerange:
4720 # TODO: should follow file history from logcmdutil._initialrevs(),
4723 # TODO: should follow file history from logcmdutil._initialrevs(),
4721 # then filter the result by logcmdutil._makerevset() and --limit
4724 # then filter the result by logcmdutil._makerevset() and --limit
4722 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4725 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4723
4726
4724 getcopies = None
4727 getcopies = None
4725 if opts.get(b'copies'):
4728 if opts.get(b'copies'):
4726 endrev = None
4729 endrev = None
4727 if revs:
4730 if revs:
4728 endrev = revs.max() + 1
4731 endrev = revs.max() + 1
4729 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4732 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4730
4733
4731 ui.pager(b'log')
4734 ui.pager(b'log')
4732 displayer = logcmdutil.changesetdisplayer(
4735 displayer = logcmdutil.changesetdisplayer(
4733 ui, repo, opts, differ, buffered=True
4736 ui, repo, opts, differ, buffered=True
4734 )
4737 )
4735 if opts.get(b'graph'):
4738 if opts.get(b'graph'):
4736 displayfn = logcmdutil.displaygraphrevs
4739 displayfn = logcmdutil.displaygraphrevs
4737 else:
4740 else:
4738 displayfn = logcmdutil.displayrevs
4741 displayfn = logcmdutil.displayrevs
4739 displayfn(ui, repo, revs, displayer, getcopies)
4742 displayfn(ui, repo, revs, displayer, getcopies)
4740
4743
4741
4744
4742 @command(
4745 @command(
4743 b'manifest',
4746 b'manifest',
4744 [
4747 [
4745 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4748 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4746 (b'', b'all', False, _(b"list files from all revisions")),
4749 (b'', b'all', False, _(b"list files from all revisions")),
4747 ]
4750 ]
4748 + formatteropts,
4751 + formatteropts,
4749 _(b'[-r REV]'),
4752 _(b'[-r REV]'),
4750 helpcategory=command.CATEGORY_MAINTENANCE,
4753 helpcategory=command.CATEGORY_MAINTENANCE,
4751 intents={INTENT_READONLY},
4754 intents={INTENT_READONLY},
4752 )
4755 )
4753 def manifest(ui, repo, node=None, rev=None, **opts):
4756 def manifest(ui, repo, node=None, rev=None, **opts):
4754 """output the current or given revision of the project manifest
4757 """output the current or given revision of the project manifest
4755
4758
4756 Print a list of version controlled files for the given revision.
4759 Print a list of version controlled files for the given revision.
4757 If no revision is given, the first parent of the working directory
4760 If no revision is given, the first parent of the working directory
4758 is used, or the null revision if no revision is checked out.
4761 is used, or the null revision if no revision is checked out.
4759
4762
4760 With -v, print file permissions, symlink and executable bits.
4763 With -v, print file permissions, symlink and executable bits.
4761 With --debug, print file revision hashes.
4764 With --debug, print file revision hashes.
4762
4765
4763 If option --all is specified, the list of all files from all revisions
4766 If option --all is specified, the list of all files from all revisions
4764 is printed. This includes deleted and renamed files.
4767 is printed. This includes deleted and renamed files.
4765
4768
4766 Returns 0 on success.
4769 Returns 0 on success.
4767 """
4770 """
4768 opts = pycompat.byteskwargs(opts)
4771 opts = pycompat.byteskwargs(opts)
4769 fm = ui.formatter(b'manifest', opts)
4772 fm = ui.formatter(b'manifest', opts)
4770
4773
4771 if opts.get(b'all'):
4774 if opts.get(b'all'):
4772 if rev or node:
4775 if rev or node:
4773 raise error.Abort(_(b"can't specify a revision with --all"))
4776 raise error.Abort(_(b"can't specify a revision with --all"))
4774
4777
4775 res = set()
4778 res = set()
4776 for rev in repo:
4779 for rev in repo:
4777 ctx = repo[rev]
4780 ctx = repo[rev]
4778 res |= set(ctx.files())
4781 res |= set(ctx.files())
4779
4782
4780 ui.pager(b'manifest')
4783 ui.pager(b'manifest')
4781 for f in sorted(res):
4784 for f in sorted(res):
4782 fm.startitem()
4785 fm.startitem()
4783 fm.write(b"path", b'%s\n', f)
4786 fm.write(b"path", b'%s\n', f)
4784 fm.end()
4787 fm.end()
4785 return
4788 return
4786
4789
4787 if rev and node:
4790 if rev and node:
4788 raise error.Abort(_(b"please specify just one revision"))
4791 raise error.Abort(_(b"please specify just one revision"))
4789
4792
4790 if not node:
4793 if not node:
4791 node = rev
4794 node = rev
4792
4795
4793 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4796 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4794 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4797 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4795 if node:
4798 if node:
4796 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4799 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4797 ctx = scmutil.revsingle(repo, node)
4800 ctx = scmutil.revsingle(repo, node)
4798 mf = ctx.manifest()
4801 mf = ctx.manifest()
4799 ui.pager(b'manifest')
4802 ui.pager(b'manifest')
4800 for f in ctx:
4803 for f in ctx:
4801 fm.startitem()
4804 fm.startitem()
4802 fm.context(ctx=ctx)
4805 fm.context(ctx=ctx)
4803 fl = ctx[f].flags()
4806 fl = ctx[f].flags()
4804 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4807 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4805 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4808 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4806 fm.write(b'path', b'%s\n', f)
4809 fm.write(b'path', b'%s\n', f)
4807 fm.end()
4810 fm.end()
4808
4811
4809
4812
4810 @command(
4813 @command(
4811 b'merge',
4814 b'merge',
4812 [
4815 [
4813 (
4816 (
4814 b'f',
4817 b'f',
4815 b'force',
4818 b'force',
4816 None,
4819 None,
4817 _(b'force a merge including outstanding changes (DEPRECATED)'),
4820 _(b'force a merge including outstanding changes (DEPRECATED)'),
4818 ),
4821 ),
4819 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4822 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4820 (
4823 (
4821 b'P',
4824 b'P',
4822 b'preview',
4825 b'preview',
4823 None,
4826 None,
4824 _(b'review revisions to merge (no merge is performed)'),
4827 _(b'review revisions to merge (no merge is performed)'),
4825 ),
4828 ),
4826 (b'', b'abort', None, _(b'abort the ongoing merge')),
4829 (b'', b'abort', None, _(b'abort the ongoing merge')),
4827 ]
4830 ]
4828 + mergetoolopts,
4831 + mergetoolopts,
4829 _(b'[-P] [[-r] REV]'),
4832 _(b'[-P] [[-r] REV]'),
4830 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4833 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4831 helpbasic=True,
4834 helpbasic=True,
4832 )
4835 )
4833 def merge(ui, repo, node=None, **opts):
4836 def merge(ui, repo, node=None, **opts):
4834 """merge another revision into working directory
4837 """merge another revision into working directory
4835
4838
4836 The current working directory is updated with all changes made in
4839 The current working directory is updated with all changes made in
4837 the requested revision since the last common predecessor revision.
4840 the requested revision since the last common predecessor revision.
4838
4841
4839 Files that changed between either parent are marked as changed for
4842 Files that changed between either parent are marked as changed for
4840 the next commit and a commit must be performed before any further
4843 the next commit and a commit must be performed before any further
4841 updates to the repository are allowed. The next commit will have
4844 updates to the repository are allowed. The next commit will have
4842 two parents.
4845 two parents.
4843
4846
4844 ``--tool`` can be used to specify the merge tool used for file
4847 ``--tool`` can be used to specify the merge tool used for file
4845 merges. It overrides the HGMERGE environment variable and your
4848 merges. It overrides the HGMERGE environment variable and your
4846 configuration files. See :hg:`help merge-tools` for options.
4849 configuration files. See :hg:`help merge-tools` for options.
4847
4850
4848 If no revision is specified, the working directory's parent is a
4851 If no revision is specified, the working directory's parent is a
4849 head revision, and the current branch contains exactly one other
4852 head revision, and the current branch contains exactly one other
4850 head, the other head is merged with by default. Otherwise, an
4853 head, the other head is merged with by default. Otherwise, an
4851 explicit revision with which to merge must be provided.
4854 explicit revision with which to merge must be provided.
4852
4855
4853 See :hg:`help resolve` for information on handling file conflicts.
4856 See :hg:`help resolve` for information on handling file conflicts.
4854
4857
4855 To undo an uncommitted merge, use :hg:`merge --abort` which
4858 To undo an uncommitted merge, use :hg:`merge --abort` which
4856 will check out a clean copy of the original merge parent, losing
4859 will check out a clean copy of the original merge parent, losing
4857 all changes.
4860 all changes.
4858
4861
4859 Returns 0 on success, 1 if there are unresolved files.
4862 Returns 0 on success, 1 if there are unresolved files.
4860 """
4863 """
4861
4864
4862 opts = pycompat.byteskwargs(opts)
4865 opts = pycompat.byteskwargs(opts)
4863 abort = opts.get(b'abort')
4866 abort = opts.get(b'abort')
4864 if abort and repo.dirstate.p2() == nullid:
4867 if abort and repo.dirstate.p2() == nullid:
4865 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4868 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4866 if abort:
4869 if abort:
4867 state = cmdutil.getunfinishedstate(repo)
4870 state = cmdutil.getunfinishedstate(repo)
4868 if state and state._opname != b'merge':
4871 if state and state._opname != b'merge':
4869 raise error.Abort(
4872 raise error.Abort(
4870 _(b'cannot abort merge with %s in progress') % (state._opname),
4873 _(b'cannot abort merge with %s in progress') % (state._opname),
4871 hint=state.hint(),
4874 hint=state.hint(),
4872 )
4875 )
4873 if node:
4876 if node:
4874 raise error.Abort(_(b"cannot specify a node with --abort"))
4877 raise error.Abort(_(b"cannot specify a node with --abort"))
4875 if opts.get(b'rev'):
4878 if opts.get(b'rev'):
4876 raise error.Abort(_(b"cannot specify both --rev and --abort"))
4879 raise error.Abort(_(b"cannot specify both --rev and --abort"))
4877 if opts.get(b'preview'):
4880 if opts.get(b'preview'):
4878 raise error.Abort(_(b"cannot specify --preview with --abort"))
4881 raise error.Abort(_(b"cannot specify --preview with --abort"))
4879 if opts.get(b'rev') and node:
4882 if opts.get(b'rev') and node:
4880 raise error.Abort(_(b"please specify just one revision"))
4883 raise error.Abort(_(b"please specify just one revision"))
4881 if not node:
4884 if not node:
4882 node = opts.get(b'rev')
4885 node = opts.get(b'rev')
4883
4886
4884 if node:
4887 if node:
4885 node = scmutil.revsingle(repo, node).node()
4888 node = scmutil.revsingle(repo, node).node()
4886
4889
4887 if not node and not abort:
4890 if not node and not abort:
4888 node = repo[destutil.destmerge(repo)].node()
4891 node = repo[destutil.destmerge(repo)].node()
4889
4892
4890 if opts.get(b'preview'):
4893 if opts.get(b'preview'):
4891 # find nodes that are ancestors of p2 but not of p1
4894 # find nodes that are ancestors of p2 but not of p1
4892 p1 = repo.lookup(b'.')
4895 p1 = repo.lookup(b'.')
4893 p2 = node
4896 p2 = node
4894 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4897 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4895
4898
4896 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4899 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4897 for node in nodes:
4900 for node in nodes:
4898 displayer.show(repo[node])
4901 displayer.show(repo[node])
4899 displayer.close()
4902 displayer.close()
4900 return 0
4903 return 0
4901
4904
4902 # ui.forcemerge is an internal variable, do not document
4905 # ui.forcemerge is an internal variable, do not document
4903 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4906 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4904 with ui.configoverride(overrides, b'merge'):
4907 with ui.configoverride(overrides, b'merge'):
4905 force = opts.get(b'force')
4908 force = opts.get(b'force')
4906 labels = [b'working copy', b'merge rev']
4909 labels = [b'working copy', b'merge rev']
4907 return hg.merge(
4910 return hg.merge(
4908 repo,
4911 repo,
4909 node,
4912 node,
4910 force=force,
4913 force=force,
4911 mergeforce=force,
4914 mergeforce=force,
4912 labels=labels,
4915 labels=labels,
4913 abort=abort,
4916 abort=abort,
4914 )
4917 )
4915
4918
4916
4919
4917 statemod.addunfinished(
4920 statemod.addunfinished(
4918 b'merge',
4921 b'merge',
4919 fname=None,
4922 fname=None,
4920 clearable=True,
4923 clearable=True,
4921 allowcommit=True,
4924 allowcommit=True,
4922 cmdmsg=_(b'outstanding uncommitted merge'),
4925 cmdmsg=_(b'outstanding uncommitted merge'),
4923 abortfunc=hg.abortmerge,
4926 abortfunc=hg.abortmerge,
4924 statushint=_(
4927 statushint=_(
4925 b'To continue: hg commit\nTo abort: hg merge --abort'
4928 b'To continue: hg commit\nTo abort: hg merge --abort'
4926 ),
4929 ),
4927 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4930 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4928 )
4931 )
4929
4932
4930
4933
4931 @command(
4934 @command(
4932 b'outgoing|out',
4935 b'outgoing|out',
4933 [
4936 [
4934 (
4937 (
4935 b'f',
4938 b'f',
4936 b'force',
4939 b'force',
4937 None,
4940 None,
4938 _(b'run even when the destination is unrelated'),
4941 _(b'run even when the destination is unrelated'),
4939 ),
4942 ),
4940 (
4943 (
4941 b'r',
4944 b'r',
4942 b'rev',
4945 b'rev',
4943 [],
4946 [],
4944 _(b'a changeset intended to be included in the destination'),
4947 _(b'a changeset intended to be included in the destination'),
4945 _(b'REV'),
4948 _(b'REV'),
4946 ),
4949 ),
4947 (b'n', b'newest-first', None, _(b'show newest record first')),
4950 (b'n', b'newest-first', None, _(b'show newest record first')),
4948 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4951 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4949 (
4952 (
4950 b'b',
4953 b'b',
4951 b'branch',
4954 b'branch',
4952 [],
4955 [],
4953 _(b'a specific branch you would like to push'),
4956 _(b'a specific branch you would like to push'),
4954 _(b'BRANCH'),
4957 _(b'BRANCH'),
4955 ),
4958 ),
4956 ]
4959 ]
4957 + logopts
4960 + logopts
4958 + remoteopts
4961 + remoteopts
4959 + subrepoopts,
4962 + subrepoopts,
4960 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4963 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4961 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4964 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4962 )
4965 )
4963 def outgoing(ui, repo, dest=None, **opts):
4966 def outgoing(ui, repo, dest=None, **opts):
4964 """show changesets not found in the destination
4967 """show changesets not found in the destination
4965
4968
4966 Show changesets not found in the specified destination repository
4969 Show changesets not found in the specified destination repository
4967 or the default push location. These are the changesets that would
4970 or the default push location. These are the changesets that would
4968 be pushed if a push was requested.
4971 be pushed if a push was requested.
4969
4972
4970 See pull for details of valid destination formats.
4973 See pull for details of valid destination formats.
4971
4974
4972 .. container:: verbose
4975 .. container:: verbose
4973
4976
4974 With -B/--bookmarks, the result of bookmark comparison between
4977 With -B/--bookmarks, the result of bookmark comparison between
4975 local and remote repositories is displayed. With -v/--verbose,
4978 local and remote repositories is displayed. With -v/--verbose,
4976 status is also displayed for each bookmark like below::
4979 status is also displayed for each bookmark like below::
4977
4980
4978 BM1 01234567890a added
4981 BM1 01234567890a added
4979 BM2 deleted
4982 BM2 deleted
4980 BM3 234567890abc advanced
4983 BM3 234567890abc advanced
4981 BM4 34567890abcd diverged
4984 BM4 34567890abcd diverged
4982 BM5 4567890abcde changed
4985 BM5 4567890abcde changed
4983
4986
4984 The action taken when pushing depends on the
4987 The action taken when pushing depends on the
4985 status of each bookmark:
4988 status of each bookmark:
4986
4989
4987 :``added``: push with ``-B`` will create it
4990 :``added``: push with ``-B`` will create it
4988 :``deleted``: push with ``-B`` will delete it
4991 :``deleted``: push with ``-B`` will delete it
4989 :``advanced``: push will update it
4992 :``advanced``: push will update it
4990 :``diverged``: push with ``-B`` will update it
4993 :``diverged``: push with ``-B`` will update it
4991 :``changed``: push with ``-B`` will update it
4994 :``changed``: push with ``-B`` will update it
4992
4995
4993 From the point of view of pushing behavior, bookmarks
4996 From the point of view of pushing behavior, bookmarks
4994 existing only in the remote repository are treated as
4997 existing only in the remote repository are treated as
4995 ``deleted``, even if it is in fact added remotely.
4998 ``deleted``, even if it is in fact added remotely.
4996
4999
4997 Returns 0 if there are outgoing changes, 1 otherwise.
5000 Returns 0 if there are outgoing changes, 1 otherwise.
4998 """
5001 """
4999 # hg._outgoing() needs to re-resolve the path in order to handle #branch
5002 # hg._outgoing() needs to re-resolve the path in order to handle #branch
5000 # style URLs, so don't overwrite dest.
5003 # style URLs, so don't overwrite dest.
5001 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5004 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5002 if not path:
5005 if not path:
5003 raise error.Abort(
5006 raise error.Abort(
5004 _(b'default repository not configured!'),
5007 _(b'default repository not configured!'),
5005 hint=_(b"see 'hg help config.paths'"),
5008 hint=_(b"see 'hg help config.paths'"),
5006 )
5009 )
5007
5010
5008 opts = pycompat.byteskwargs(opts)
5011 opts = pycompat.byteskwargs(opts)
5009 if opts.get(b'graph'):
5012 if opts.get(b'graph'):
5010 logcmdutil.checkunsupportedgraphflags([], opts)
5013 logcmdutil.checkunsupportedgraphflags([], opts)
5011 o, other = hg._outgoing(ui, repo, dest, opts)
5014 o, other = hg._outgoing(ui, repo, dest, opts)
5012 if not o:
5015 if not o:
5013 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5016 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5014 return
5017 return
5015
5018
5016 revdag = logcmdutil.graphrevs(repo, o, opts)
5019 revdag = logcmdutil.graphrevs(repo, o, opts)
5017 ui.pager(b'outgoing')
5020 ui.pager(b'outgoing')
5018 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5021 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5019 logcmdutil.displaygraph(
5022 logcmdutil.displaygraph(
5020 ui, repo, revdag, displayer, graphmod.asciiedges
5023 ui, repo, revdag, displayer, graphmod.asciiedges
5021 )
5024 )
5022 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5025 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5023 return 0
5026 return 0
5024
5027
5025 if opts.get(b'bookmarks'):
5028 if opts.get(b'bookmarks'):
5026 dest = path.pushloc or path.loc
5029 dest = path.pushloc or path.loc
5027 other = hg.peer(repo, opts, dest)
5030 other = hg.peer(repo, opts, dest)
5028 if b'bookmarks' not in other.listkeys(b'namespaces'):
5031 if b'bookmarks' not in other.listkeys(b'namespaces'):
5029 ui.warn(_(b"remote doesn't support bookmarks\n"))
5032 ui.warn(_(b"remote doesn't support bookmarks\n"))
5030 return 0
5033 return 0
5031 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5034 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5032 ui.pager(b'outgoing')
5035 ui.pager(b'outgoing')
5033 return bookmarks.outgoing(ui, repo, other)
5036 return bookmarks.outgoing(ui, repo, other)
5034
5037
5035 repo._subtoppath = path.pushloc or path.loc
5038 repo._subtoppath = path.pushloc or path.loc
5036 try:
5039 try:
5037 return hg.outgoing(ui, repo, dest, opts)
5040 return hg.outgoing(ui, repo, dest, opts)
5038 finally:
5041 finally:
5039 del repo._subtoppath
5042 del repo._subtoppath
5040
5043
5041
5044
5042 @command(
5045 @command(
5043 b'parents',
5046 b'parents',
5044 [
5047 [
5045 (
5048 (
5046 b'r',
5049 b'r',
5047 b'rev',
5050 b'rev',
5048 b'',
5051 b'',
5049 _(b'show parents of the specified revision'),
5052 _(b'show parents of the specified revision'),
5050 _(b'REV'),
5053 _(b'REV'),
5051 ),
5054 ),
5052 ]
5055 ]
5053 + templateopts,
5056 + templateopts,
5054 _(b'[-r REV] [FILE]'),
5057 _(b'[-r REV] [FILE]'),
5055 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5058 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5056 inferrepo=True,
5059 inferrepo=True,
5057 )
5060 )
5058 def parents(ui, repo, file_=None, **opts):
5061 def parents(ui, repo, file_=None, **opts):
5059 """show the parents of the working directory or revision (DEPRECATED)
5062 """show the parents of the working directory or revision (DEPRECATED)
5060
5063
5061 Print the working directory's parent revisions. If a revision is
5064 Print the working directory's parent revisions. If a revision is
5062 given via -r/--rev, the parent of that revision will be printed.
5065 given via -r/--rev, the parent of that revision will be printed.
5063 If a file argument is given, the revision in which the file was
5066 If a file argument is given, the revision in which the file was
5064 last changed (before the working directory revision or the
5067 last changed (before the working directory revision or the
5065 argument to --rev if given) is printed.
5068 argument to --rev if given) is printed.
5066
5069
5067 This command is equivalent to::
5070 This command is equivalent to::
5068
5071
5069 hg log -r "p1()+p2()" or
5072 hg log -r "p1()+p2()" or
5070 hg log -r "p1(REV)+p2(REV)" or
5073 hg log -r "p1(REV)+p2(REV)" or
5071 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5074 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5072 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5075 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5073
5076
5074 See :hg:`summary` and :hg:`help revsets` for related information.
5077 See :hg:`summary` and :hg:`help revsets` for related information.
5075
5078
5076 Returns 0 on success.
5079 Returns 0 on success.
5077 """
5080 """
5078
5081
5079 opts = pycompat.byteskwargs(opts)
5082 opts = pycompat.byteskwargs(opts)
5080 rev = opts.get(b'rev')
5083 rev = opts.get(b'rev')
5081 if rev:
5084 if rev:
5082 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5085 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5083 ctx = scmutil.revsingle(repo, rev, None)
5086 ctx = scmutil.revsingle(repo, rev, None)
5084
5087
5085 if file_:
5088 if file_:
5086 m = scmutil.match(ctx, (file_,), opts)
5089 m = scmutil.match(ctx, (file_,), opts)
5087 if m.anypats() or len(m.files()) != 1:
5090 if m.anypats() or len(m.files()) != 1:
5088 raise error.Abort(_(b'can only specify an explicit filename'))
5091 raise error.Abort(_(b'can only specify an explicit filename'))
5089 file_ = m.files()[0]
5092 file_ = m.files()[0]
5090 filenodes = []
5093 filenodes = []
5091 for cp in ctx.parents():
5094 for cp in ctx.parents():
5092 if not cp:
5095 if not cp:
5093 continue
5096 continue
5094 try:
5097 try:
5095 filenodes.append(cp.filenode(file_))
5098 filenodes.append(cp.filenode(file_))
5096 except error.LookupError:
5099 except error.LookupError:
5097 pass
5100 pass
5098 if not filenodes:
5101 if not filenodes:
5099 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5102 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5100 p = []
5103 p = []
5101 for fn in filenodes:
5104 for fn in filenodes:
5102 fctx = repo.filectx(file_, fileid=fn)
5105 fctx = repo.filectx(file_, fileid=fn)
5103 p.append(fctx.node())
5106 p.append(fctx.node())
5104 else:
5107 else:
5105 p = [cp.node() for cp in ctx.parents()]
5108 p = [cp.node() for cp in ctx.parents()]
5106
5109
5107 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5110 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5108 for n in p:
5111 for n in p:
5109 if n != nullid:
5112 if n != nullid:
5110 displayer.show(repo[n])
5113 displayer.show(repo[n])
5111 displayer.close()
5114 displayer.close()
5112
5115
5113
5116
5114 @command(
5117 @command(
5115 b'paths',
5118 b'paths',
5116 formatteropts,
5119 formatteropts,
5117 _(b'[NAME]'),
5120 _(b'[NAME]'),
5118 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5121 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5119 optionalrepo=True,
5122 optionalrepo=True,
5120 intents={INTENT_READONLY},
5123 intents={INTENT_READONLY},
5121 )
5124 )
5122 def paths(ui, repo, search=None, **opts):
5125 def paths(ui, repo, search=None, **opts):
5123 """show aliases for remote repositories
5126 """show aliases for remote repositories
5124
5127
5125 Show definition of symbolic path name NAME. If no name is given,
5128 Show definition of symbolic path name NAME. If no name is given,
5126 show definition of all available names.
5129 show definition of all available names.
5127
5130
5128 Option -q/--quiet suppresses all output when searching for NAME
5131 Option -q/--quiet suppresses all output when searching for NAME
5129 and shows only the path names when listing all definitions.
5132 and shows only the path names when listing all definitions.
5130
5133
5131 Path names are defined in the [paths] section of your
5134 Path names are defined in the [paths] section of your
5132 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5135 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5133 repository, ``.hg/hgrc`` is used, too.
5136 repository, ``.hg/hgrc`` is used, too.
5134
5137
5135 The path names ``default`` and ``default-push`` have a special
5138 The path names ``default`` and ``default-push`` have a special
5136 meaning. When performing a push or pull operation, they are used
5139 meaning. When performing a push or pull operation, they are used
5137 as fallbacks if no location is specified on the command-line.
5140 as fallbacks if no location is specified on the command-line.
5138 When ``default-push`` is set, it will be used for push and
5141 When ``default-push`` is set, it will be used for push and
5139 ``default`` will be used for pull; otherwise ``default`` is used
5142 ``default`` will be used for pull; otherwise ``default`` is used
5140 as the fallback for both. When cloning a repository, the clone
5143 as the fallback for both. When cloning a repository, the clone
5141 source is written as ``default`` in ``.hg/hgrc``.
5144 source is written as ``default`` in ``.hg/hgrc``.
5142
5145
5143 .. note::
5146 .. note::
5144
5147
5145 ``default`` and ``default-push`` apply to all inbound (e.g.
5148 ``default`` and ``default-push`` apply to all inbound (e.g.
5146 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5149 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5147 and :hg:`bundle`) operations.
5150 and :hg:`bundle`) operations.
5148
5151
5149 See :hg:`help urls` for more information.
5152 See :hg:`help urls` for more information.
5150
5153
5151 .. container:: verbose
5154 .. container:: verbose
5152
5155
5153 Template:
5156 Template:
5154
5157
5155 The following keywords are supported. See also :hg:`help templates`.
5158 The following keywords are supported. See also :hg:`help templates`.
5156
5159
5157 :name: String. Symbolic name of the path alias.
5160 :name: String. Symbolic name of the path alias.
5158 :pushurl: String. URL for push operations.
5161 :pushurl: String. URL for push operations.
5159 :url: String. URL or directory path for the other operations.
5162 :url: String. URL or directory path for the other operations.
5160
5163
5161 Returns 0 on success.
5164 Returns 0 on success.
5162 """
5165 """
5163
5166
5164 opts = pycompat.byteskwargs(opts)
5167 opts = pycompat.byteskwargs(opts)
5165 ui.pager(b'paths')
5168 ui.pager(b'paths')
5166 if search:
5169 if search:
5167 pathitems = [
5170 pathitems = [
5168 (name, path)
5171 (name, path)
5169 for name, path in pycompat.iteritems(ui.paths)
5172 for name, path in pycompat.iteritems(ui.paths)
5170 if name == search
5173 if name == search
5171 ]
5174 ]
5172 else:
5175 else:
5173 pathitems = sorted(pycompat.iteritems(ui.paths))
5176 pathitems = sorted(pycompat.iteritems(ui.paths))
5174
5177
5175 fm = ui.formatter(b'paths', opts)
5178 fm = ui.formatter(b'paths', opts)
5176 if fm.isplain():
5179 if fm.isplain():
5177 hidepassword = util.hidepassword
5180 hidepassword = util.hidepassword
5178 else:
5181 else:
5179 hidepassword = bytes
5182 hidepassword = bytes
5180 if ui.quiet:
5183 if ui.quiet:
5181 namefmt = b'%s\n'
5184 namefmt = b'%s\n'
5182 else:
5185 else:
5183 namefmt = b'%s = '
5186 namefmt = b'%s = '
5184 showsubopts = not search and not ui.quiet
5187 showsubopts = not search and not ui.quiet
5185
5188
5186 for name, path in pathitems:
5189 for name, path in pathitems:
5187 fm.startitem()
5190 fm.startitem()
5188 fm.condwrite(not search, b'name', namefmt, name)
5191 fm.condwrite(not search, b'name', namefmt, name)
5189 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5192 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5190 for subopt, value in sorted(path.suboptions.items()):
5193 for subopt, value in sorted(path.suboptions.items()):
5191 assert subopt not in (b'name', b'url')
5194 assert subopt not in (b'name', b'url')
5192 if showsubopts:
5195 if showsubopts:
5193 fm.plain(b'%s:%s = ' % (name, subopt))
5196 fm.plain(b'%s:%s = ' % (name, subopt))
5194 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5197 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5195
5198
5196 fm.end()
5199 fm.end()
5197
5200
5198 if search and not pathitems:
5201 if search and not pathitems:
5199 if not ui.quiet:
5202 if not ui.quiet:
5200 ui.warn(_(b"not found!\n"))
5203 ui.warn(_(b"not found!\n"))
5201 return 1
5204 return 1
5202 else:
5205 else:
5203 return 0
5206 return 0
5204
5207
5205
5208
5206 @command(
5209 @command(
5207 b'phase',
5210 b'phase',
5208 [
5211 [
5209 (b'p', b'public', False, _(b'set changeset phase to public')),
5212 (b'p', b'public', False, _(b'set changeset phase to public')),
5210 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5213 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5211 (b's', b'secret', False, _(b'set changeset phase to secret')),
5214 (b's', b'secret', False, _(b'set changeset phase to secret')),
5212 (b'f', b'force', False, _(b'allow to move boundary backward')),
5215 (b'f', b'force', False, _(b'allow to move boundary backward')),
5213 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5216 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5214 ],
5217 ],
5215 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5218 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5216 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5219 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5217 )
5220 )
5218 def phase(ui, repo, *revs, **opts):
5221 def phase(ui, repo, *revs, **opts):
5219 """set or show the current phase name
5222 """set or show the current phase name
5220
5223
5221 With no argument, show the phase name of the current revision(s).
5224 With no argument, show the phase name of the current revision(s).
5222
5225
5223 With one of -p/--public, -d/--draft or -s/--secret, change the
5226 With one of -p/--public, -d/--draft or -s/--secret, change the
5224 phase value of the specified revisions.
5227 phase value of the specified revisions.
5225
5228
5226 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5229 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5227 lower phase to a higher phase. Phases are ordered as follows::
5230 lower phase to a higher phase. Phases are ordered as follows::
5228
5231
5229 public < draft < secret
5232 public < draft < secret
5230
5233
5231 Returns 0 on success, 1 if some phases could not be changed.
5234 Returns 0 on success, 1 if some phases could not be changed.
5232
5235
5233 (For more information about the phases concept, see :hg:`help phases`.)
5236 (For more information about the phases concept, see :hg:`help phases`.)
5234 """
5237 """
5235 opts = pycompat.byteskwargs(opts)
5238 opts = pycompat.byteskwargs(opts)
5236 # search for a unique phase argument
5239 # search for a unique phase argument
5237 targetphase = None
5240 targetphase = None
5238 for idx, name in enumerate(phases.cmdphasenames):
5241 for idx, name in enumerate(phases.cmdphasenames):
5239 if opts[name]:
5242 if opts[name]:
5240 if targetphase is not None:
5243 if targetphase is not None:
5241 raise error.Abort(_(b'only one phase can be specified'))
5244 raise error.Abort(_(b'only one phase can be specified'))
5242 targetphase = idx
5245 targetphase = idx
5243
5246
5244 # look for specified revision
5247 # look for specified revision
5245 revs = list(revs)
5248 revs = list(revs)
5246 revs.extend(opts[b'rev'])
5249 revs.extend(opts[b'rev'])
5247 if not revs:
5250 if not revs:
5248 # display both parents as the second parent phase can influence
5251 # display both parents as the second parent phase can influence
5249 # the phase of a merge commit
5252 # the phase of a merge commit
5250 revs = [c.rev() for c in repo[None].parents()]
5253 revs = [c.rev() for c in repo[None].parents()]
5251
5254
5252 revs = scmutil.revrange(repo, revs)
5255 revs = scmutil.revrange(repo, revs)
5253
5256
5254 ret = 0
5257 ret = 0
5255 if targetphase is None:
5258 if targetphase is None:
5256 # display
5259 # display
5257 for r in revs:
5260 for r in revs:
5258 ctx = repo[r]
5261 ctx = repo[r]
5259 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5262 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5260 else:
5263 else:
5261 with repo.lock(), repo.transaction(b"phase") as tr:
5264 with repo.lock(), repo.transaction(b"phase") as tr:
5262 # set phase
5265 # set phase
5263 if not revs:
5266 if not revs:
5264 raise error.Abort(_(b'empty revision set'))
5267 raise error.Abort(_(b'empty revision set'))
5265 nodes = [repo[r].node() for r in revs]
5268 nodes = [repo[r].node() for r in revs]
5266 # moving revision from public to draft may hide them
5269 # moving revision from public to draft may hide them
5267 # We have to check result on an unfiltered repository
5270 # We have to check result on an unfiltered repository
5268 unfi = repo.unfiltered()
5271 unfi = repo.unfiltered()
5269 getphase = unfi._phasecache.phase
5272 getphase = unfi._phasecache.phase
5270 olddata = [getphase(unfi, r) for r in unfi]
5273 olddata = [getphase(unfi, r) for r in unfi]
5271 phases.advanceboundary(repo, tr, targetphase, nodes)
5274 phases.advanceboundary(repo, tr, targetphase, nodes)
5272 if opts[b'force']:
5275 if opts[b'force']:
5273 phases.retractboundary(repo, tr, targetphase, nodes)
5276 phases.retractboundary(repo, tr, targetphase, nodes)
5274 getphase = unfi._phasecache.phase
5277 getphase = unfi._phasecache.phase
5275 newdata = [getphase(unfi, r) for r in unfi]
5278 newdata = [getphase(unfi, r) for r in unfi]
5276 changes = sum(newdata[r] != olddata[r] for r in unfi)
5279 changes = sum(newdata[r] != olddata[r] for r in unfi)
5277 cl = unfi.changelog
5280 cl = unfi.changelog
5278 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5281 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5279 if rejected:
5282 if rejected:
5280 ui.warn(
5283 ui.warn(
5281 _(
5284 _(
5282 b'cannot move %i changesets to a higher '
5285 b'cannot move %i changesets to a higher '
5283 b'phase, use --force\n'
5286 b'phase, use --force\n'
5284 )
5287 )
5285 % len(rejected)
5288 % len(rejected)
5286 )
5289 )
5287 ret = 1
5290 ret = 1
5288 if changes:
5291 if changes:
5289 msg = _(b'phase changed for %i changesets\n') % changes
5292 msg = _(b'phase changed for %i changesets\n') % changes
5290 if ret:
5293 if ret:
5291 ui.status(msg)
5294 ui.status(msg)
5292 else:
5295 else:
5293 ui.note(msg)
5296 ui.note(msg)
5294 else:
5297 else:
5295 ui.warn(_(b'no phases changed\n'))
5298 ui.warn(_(b'no phases changed\n'))
5296 return ret
5299 return ret
5297
5300
5298
5301
5299 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5302 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5300 """Run after a changegroup has been added via pull/unbundle
5303 """Run after a changegroup has been added via pull/unbundle
5301
5304
5302 This takes arguments below:
5305 This takes arguments below:
5303
5306
5304 :modheads: change of heads by pull/unbundle
5307 :modheads: change of heads by pull/unbundle
5305 :optupdate: updating working directory is needed or not
5308 :optupdate: updating working directory is needed or not
5306 :checkout: update destination revision (or None to default destination)
5309 :checkout: update destination revision (or None to default destination)
5307 :brev: a name, which might be a bookmark to be activated after updating
5310 :brev: a name, which might be a bookmark to be activated after updating
5308 """
5311 """
5309 if modheads == 0:
5312 if modheads == 0:
5310 return
5313 return
5311 if optupdate:
5314 if optupdate:
5312 try:
5315 try:
5313 return hg.updatetotally(ui, repo, checkout, brev)
5316 return hg.updatetotally(ui, repo, checkout, brev)
5314 except error.UpdateAbort as inst:
5317 except error.UpdateAbort as inst:
5315 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5318 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5316 hint = inst.hint
5319 hint = inst.hint
5317 raise error.UpdateAbort(msg, hint=hint)
5320 raise error.UpdateAbort(msg, hint=hint)
5318 if modheads is not None and modheads > 1:
5321 if modheads is not None and modheads > 1:
5319 currentbranchheads = len(repo.branchheads())
5322 currentbranchheads = len(repo.branchheads())
5320 if currentbranchheads == modheads:
5323 if currentbranchheads == modheads:
5321 ui.status(
5324 ui.status(
5322 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5325 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5323 )
5326 )
5324 elif currentbranchheads > 1:
5327 elif currentbranchheads > 1:
5325 ui.status(
5328 ui.status(
5326 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5329 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5327 )
5330 )
5328 else:
5331 else:
5329 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5332 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5330 elif not ui.configbool(b'commands', b'update.requiredest'):
5333 elif not ui.configbool(b'commands', b'update.requiredest'):
5331 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5334 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5332
5335
5333
5336
5334 @command(
5337 @command(
5335 b'pull',
5338 b'pull',
5336 [
5339 [
5337 (
5340 (
5338 b'u',
5341 b'u',
5339 b'update',
5342 b'update',
5340 None,
5343 None,
5341 _(b'update to new branch head if new descendants were pulled'),
5344 _(b'update to new branch head if new descendants were pulled'),
5342 ),
5345 ),
5343 (
5346 (
5344 b'f',
5347 b'f',
5345 b'force',
5348 b'force',
5346 None,
5349 None,
5347 _(b'run even when remote repository is unrelated'),
5350 _(b'run even when remote repository is unrelated'),
5348 ),
5351 ),
5349 (
5352 (
5350 b'r',
5353 b'r',
5351 b'rev',
5354 b'rev',
5352 [],
5355 [],
5353 _(b'a remote changeset intended to be added'),
5356 _(b'a remote changeset intended to be added'),
5354 _(b'REV'),
5357 _(b'REV'),
5355 ),
5358 ),
5356 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5359 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5357 (
5360 (
5358 b'b',
5361 b'b',
5359 b'branch',
5362 b'branch',
5360 [],
5363 [],
5361 _(b'a specific branch you would like to pull'),
5364 _(b'a specific branch you would like to pull'),
5362 _(b'BRANCH'),
5365 _(b'BRANCH'),
5363 ),
5366 ),
5364 ]
5367 ]
5365 + remoteopts,
5368 + remoteopts,
5366 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5369 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5367 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5370 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5368 helpbasic=True,
5371 helpbasic=True,
5369 )
5372 )
5370 def pull(ui, repo, source=b"default", **opts):
5373 def pull(ui, repo, source=b"default", **opts):
5371 """pull changes from the specified source
5374 """pull changes from the specified source
5372
5375
5373 Pull changes from a remote repository to a local one.
5376 Pull changes from a remote repository to a local one.
5374
5377
5375 This finds all changes from the repository at the specified path
5378 This finds all changes from the repository at the specified path
5376 or URL and adds them to a local repository (the current one unless
5379 or URL and adds them to a local repository (the current one unless
5377 -R is specified). By default, this does not update the copy of the
5380 -R is specified). By default, this does not update the copy of the
5378 project in the working directory.
5381 project in the working directory.
5379
5382
5380 When cloning from servers that support it, Mercurial may fetch
5383 When cloning from servers that support it, Mercurial may fetch
5381 pre-generated data. When this is done, hooks operating on incoming
5384 pre-generated data. When this is done, hooks operating on incoming
5382 changesets and changegroups may fire more than once, once for each
5385 changesets and changegroups may fire more than once, once for each
5383 pre-generated bundle and as well as for any additional remaining
5386 pre-generated bundle and as well as for any additional remaining
5384 data. See :hg:`help -e clonebundles` for more.
5387 data. See :hg:`help -e clonebundles` for more.
5385
5388
5386 Use :hg:`incoming` if you want to see what would have been added
5389 Use :hg:`incoming` if you want to see what would have been added
5387 by a pull at the time you issued this command. If you then decide
5390 by a pull at the time you issued this command. If you then decide
5388 to add those changes to the repository, you should use :hg:`pull
5391 to add those changes to the repository, you should use :hg:`pull
5389 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5392 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5390
5393
5391 If SOURCE is omitted, the 'default' path will be used.
5394 If SOURCE is omitted, the 'default' path will be used.
5392 See :hg:`help urls` for more information.
5395 See :hg:`help urls` for more information.
5393
5396
5394 Specifying bookmark as ``.`` is equivalent to specifying the active
5397 Specifying bookmark as ``.`` is equivalent to specifying the active
5395 bookmark's name.
5398 bookmark's name.
5396
5399
5397 Returns 0 on success, 1 if an update had unresolved files.
5400 Returns 0 on success, 1 if an update had unresolved files.
5398 """
5401 """
5399
5402
5400 opts = pycompat.byteskwargs(opts)
5403 opts = pycompat.byteskwargs(opts)
5401 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5404 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5402 b'update'
5405 b'update'
5403 ):
5406 ):
5404 msg = _(b'update destination required by configuration')
5407 msg = _(b'update destination required by configuration')
5405 hint = _(b'use hg pull followed by hg update DEST')
5408 hint = _(b'use hg pull followed by hg update DEST')
5406 raise error.Abort(msg, hint=hint)
5409 raise error.Abort(msg, hint=hint)
5407
5410
5408 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5411 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5409 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5412 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5410 other = hg.peer(repo, opts, source)
5413 other = hg.peer(repo, opts, source)
5411 try:
5414 try:
5412 revs, checkout = hg.addbranchrevs(
5415 revs, checkout = hg.addbranchrevs(
5413 repo, other, branches, opts.get(b'rev')
5416 repo, other, branches, opts.get(b'rev')
5414 )
5417 )
5415
5418
5416 pullopargs = {}
5419 pullopargs = {}
5417
5420
5418 nodes = None
5421 nodes = None
5419 if opts.get(b'bookmark') or revs:
5422 if opts.get(b'bookmark') or revs:
5420 # The list of bookmark used here is the same used to actually update
5423 # The list of bookmark used here is the same used to actually update
5421 # the bookmark names, to avoid the race from issue 4689 and we do
5424 # the bookmark names, to avoid the race from issue 4689 and we do
5422 # all lookup and bookmark queries in one go so they see the same
5425 # all lookup and bookmark queries in one go so they see the same
5423 # version of the server state (issue 4700).
5426 # version of the server state (issue 4700).
5424 nodes = []
5427 nodes = []
5425 fnodes = []
5428 fnodes = []
5426 revs = revs or []
5429 revs = revs or []
5427 if revs and not other.capable(b'lookup'):
5430 if revs and not other.capable(b'lookup'):
5428 err = _(
5431 err = _(
5429 b"other repository doesn't support revision lookup, "
5432 b"other repository doesn't support revision lookup, "
5430 b"so a rev cannot be specified."
5433 b"so a rev cannot be specified."
5431 )
5434 )
5432 raise error.Abort(err)
5435 raise error.Abort(err)
5433 with other.commandexecutor() as e:
5436 with other.commandexecutor() as e:
5434 fremotebookmarks = e.callcommand(
5437 fremotebookmarks = e.callcommand(
5435 b'listkeys', {b'namespace': b'bookmarks'}
5438 b'listkeys', {b'namespace': b'bookmarks'}
5436 )
5439 )
5437 for r in revs:
5440 for r in revs:
5438 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5441 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5439 remotebookmarks = fremotebookmarks.result()
5442 remotebookmarks = fremotebookmarks.result()
5440 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5443 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5441 pullopargs[b'remotebookmarks'] = remotebookmarks
5444 pullopargs[b'remotebookmarks'] = remotebookmarks
5442 for b in opts.get(b'bookmark', []):
5445 for b in opts.get(b'bookmark', []):
5443 b = repo._bookmarks.expandname(b)
5446 b = repo._bookmarks.expandname(b)
5444 if b not in remotebookmarks:
5447 if b not in remotebookmarks:
5445 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5448 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5446 nodes.append(remotebookmarks[b])
5449 nodes.append(remotebookmarks[b])
5447 for i, rev in enumerate(revs):
5450 for i, rev in enumerate(revs):
5448 node = fnodes[i].result()
5451 node = fnodes[i].result()
5449 nodes.append(node)
5452 nodes.append(node)
5450 if rev == checkout:
5453 if rev == checkout:
5451 checkout = node
5454 checkout = node
5452
5455
5453 wlock = util.nullcontextmanager()
5456 wlock = util.nullcontextmanager()
5454 if opts.get(b'update'):
5457 if opts.get(b'update'):
5455 wlock = repo.wlock()
5458 wlock = repo.wlock()
5456 with wlock:
5459 with wlock:
5457 pullopargs.update(opts.get(b'opargs', {}))
5460 pullopargs.update(opts.get(b'opargs', {}))
5458 modheads = exchange.pull(
5461 modheads = exchange.pull(
5459 repo,
5462 repo,
5460 other,
5463 other,
5461 heads=nodes,
5464 heads=nodes,
5462 force=opts.get(b'force'),
5465 force=opts.get(b'force'),
5463 bookmarks=opts.get(b'bookmark', ()),
5466 bookmarks=opts.get(b'bookmark', ()),
5464 opargs=pullopargs,
5467 opargs=pullopargs,
5465 ).cgresult
5468 ).cgresult
5466
5469
5467 # brev is a name, which might be a bookmark to be activated at
5470 # brev is a name, which might be a bookmark to be activated at
5468 # the end of the update. In other words, it is an explicit
5471 # the end of the update. In other words, it is an explicit
5469 # destination of the update
5472 # destination of the update
5470 brev = None
5473 brev = None
5471
5474
5472 if checkout:
5475 if checkout:
5473 checkout = repo.unfiltered().changelog.rev(checkout)
5476 checkout = repo.unfiltered().changelog.rev(checkout)
5474
5477
5475 # order below depends on implementation of
5478 # order below depends on implementation of
5476 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5479 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5477 # because 'checkout' is determined without it.
5480 # because 'checkout' is determined without it.
5478 if opts.get(b'rev'):
5481 if opts.get(b'rev'):
5479 brev = opts[b'rev'][0]
5482 brev = opts[b'rev'][0]
5480 elif opts.get(b'branch'):
5483 elif opts.get(b'branch'):
5481 brev = opts[b'branch'][0]
5484 brev = opts[b'branch'][0]
5482 else:
5485 else:
5483 brev = branches[0]
5486 brev = branches[0]
5484 repo._subtoppath = source
5487 repo._subtoppath = source
5485 try:
5488 try:
5486 ret = postincoming(
5489 ret = postincoming(
5487 ui, repo, modheads, opts.get(b'update'), checkout, brev
5490 ui, repo, modheads, opts.get(b'update'), checkout, brev
5488 )
5491 )
5489 except error.FilteredRepoLookupError as exc:
5492 except error.FilteredRepoLookupError as exc:
5490 msg = _(b'cannot update to target: %s') % exc.args[0]
5493 msg = _(b'cannot update to target: %s') % exc.args[0]
5491 exc.args = (msg,) + exc.args[1:]
5494 exc.args = (msg,) + exc.args[1:]
5492 raise
5495 raise
5493 finally:
5496 finally:
5494 del repo._subtoppath
5497 del repo._subtoppath
5495
5498
5496 finally:
5499 finally:
5497 other.close()
5500 other.close()
5498 return ret
5501 return ret
5499
5502
5500
5503
5501 @command(
5504 @command(
5502 b'push',
5505 b'push',
5503 [
5506 [
5504 (b'f', b'force', None, _(b'force push')),
5507 (b'f', b'force', None, _(b'force push')),
5505 (
5508 (
5506 b'r',
5509 b'r',
5507 b'rev',
5510 b'rev',
5508 [],
5511 [],
5509 _(b'a changeset intended to be included in the destination'),
5512 _(b'a changeset intended to be included in the destination'),
5510 _(b'REV'),
5513 _(b'REV'),
5511 ),
5514 ),
5512 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5515 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5513 (
5516 (
5514 b'b',
5517 b'b',
5515 b'branch',
5518 b'branch',
5516 [],
5519 [],
5517 _(b'a specific branch you would like to push'),
5520 _(b'a specific branch you would like to push'),
5518 _(b'BRANCH'),
5521 _(b'BRANCH'),
5519 ),
5522 ),
5520 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5523 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5521 (
5524 (
5522 b'',
5525 b'',
5523 b'pushvars',
5526 b'pushvars',
5524 [],
5527 [],
5525 _(b'variables that can be sent to server (ADVANCED)'),
5528 _(b'variables that can be sent to server (ADVANCED)'),
5526 ),
5529 ),
5527 (
5530 (
5528 b'',
5531 b'',
5529 b'publish',
5532 b'publish',
5530 False,
5533 False,
5531 _(b'push the changeset as public (EXPERIMENTAL)'),
5534 _(b'push the changeset as public (EXPERIMENTAL)'),
5532 ),
5535 ),
5533 ]
5536 ]
5534 + remoteopts,
5537 + remoteopts,
5535 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5538 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5536 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5539 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5537 helpbasic=True,
5540 helpbasic=True,
5538 )
5541 )
5539 def push(ui, repo, dest=None, **opts):
5542 def push(ui, repo, dest=None, **opts):
5540 """push changes to the specified destination
5543 """push changes to the specified destination
5541
5544
5542 Push changesets from the local repository to the specified
5545 Push changesets from the local repository to the specified
5543 destination.
5546 destination.
5544
5547
5545 This operation is symmetrical to pull: it is identical to a pull
5548 This operation is symmetrical to pull: it is identical to a pull
5546 in the destination repository from the current one.
5549 in the destination repository from the current one.
5547
5550
5548 By default, push will not allow creation of new heads at the
5551 By default, push will not allow creation of new heads at the
5549 destination, since multiple heads would make it unclear which head
5552 destination, since multiple heads would make it unclear which head
5550 to use. In this situation, it is recommended to pull and merge
5553 to use. In this situation, it is recommended to pull and merge
5551 before pushing.
5554 before pushing.
5552
5555
5553 Use --new-branch if you want to allow push to create a new named
5556 Use --new-branch if you want to allow push to create a new named
5554 branch that is not present at the destination. This allows you to
5557 branch that is not present at the destination. This allows you to
5555 only create a new branch without forcing other changes.
5558 only create a new branch without forcing other changes.
5556
5559
5557 .. note::
5560 .. note::
5558
5561
5559 Extra care should be taken with the -f/--force option,
5562 Extra care should be taken with the -f/--force option,
5560 which will push all new heads on all branches, an action which will
5563 which will push all new heads on all branches, an action which will
5561 almost always cause confusion for collaborators.
5564 almost always cause confusion for collaborators.
5562
5565
5563 If -r/--rev is used, the specified revision and all its ancestors
5566 If -r/--rev is used, the specified revision and all its ancestors
5564 will be pushed to the remote repository.
5567 will be pushed to the remote repository.
5565
5568
5566 If -B/--bookmark is used, the specified bookmarked revision, its
5569 If -B/--bookmark is used, the specified bookmarked revision, its
5567 ancestors, and the bookmark will be pushed to the remote
5570 ancestors, and the bookmark will be pushed to the remote
5568 repository. Specifying ``.`` is equivalent to specifying the active
5571 repository. Specifying ``.`` is equivalent to specifying the active
5569 bookmark's name.
5572 bookmark's name.
5570
5573
5571 Please see :hg:`help urls` for important details about ``ssh://``
5574 Please see :hg:`help urls` for important details about ``ssh://``
5572 URLs. If DESTINATION is omitted, a default path will be used.
5575 URLs. If DESTINATION is omitted, a default path will be used.
5573
5576
5574 .. container:: verbose
5577 .. container:: verbose
5575
5578
5576 The --pushvars option sends strings to the server that become
5579 The --pushvars option sends strings to the server that become
5577 environment variables prepended with ``HG_USERVAR_``. For example,
5580 environment variables prepended with ``HG_USERVAR_``. For example,
5578 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5581 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5579 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5582 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5580
5583
5581 pushvars can provide for user-overridable hooks as well as set debug
5584 pushvars can provide for user-overridable hooks as well as set debug
5582 levels. One example is having a hook that blocks commits containing
5585 levels. One example is having a hook that blocks commits containing
5583 conflict markers, but enables the user to override the hook if the file
5586 conflict markers, but enables the user to override the hook if the file
5584 is using conflict markers for testing purposes or the file format has
5587 is using conflict markers for testing purposes or the file format has
5585 strings that look like conflict markers.
5588 strings that look like conflict markers.
5586
5589
5587 By default, servers will ignore `--pushvars`. To enable it add the
5590 By default, servers will ignore `--pushvars`. To enable it add the
5588 following to your configuration file::
5591 following to your configuration file::
5589
5592
5590 [push]
5593 [push]
5591 pushvars.server = true
5594 pushvars.server = true
5592
5595
5593 Returns 0 if push was successful, 1 if nothing to push.
5596 Returns 0 if push was successful, 1 if nothing to push.
5594 """
5597 """
5595
5598
5596 opts = pycompat.byteskwargs(opts)
5599 opts = pycompat.byteskwargs(opts)
5597 if opts.get(b'bookmark'):
5600 if opts.get(b'bookmark'):
5598 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5601 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5599 for b in opts[b'bookmark']:
5602 for b in opts[b'bookmark']:
5600 # translate -B options to -r so changesets get pushed
5603 # translate -B options to -r so changesets get pushed
5601 b = repo._bookmarks.expandname(b)
5604 b = repo._bookmarks.expandname(b)
5602 if b in repo._bookmarks:
5605 if b in repo._bookmarks:
5603 opts.setdefault(b'rev', []).append(b)
5606 opts.setdefault(b'rev', []).append(b)
5604 else:
5607 else:
5605 # if we try to push a deleted bookmark, translate it to null
5608 # if we try to push a deleted bookmark, translate it to null
5606 # this lets simultaneous -r, -b options continue working
5609 # this lets simultaneous -r, -b options continue working
5607 opts.setdefault(b'rev', []).append(b"null")
5610 opts.setdefault(b'rev', []).append(b"null")
5608
5611
5609 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5612 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5610 if not path:
5613 if not path:
5611 raise error.Abort(
5614 raise error.Abort(
5612 _(b'default repository not configured!'),
5615 _(b'default repository not configured!'),
5613 hint=_(b"see 'hg help config.paths'"),
5616 hint=_(b"see 'hg help config.paths'"),
5614 )
5617 )
5615 dest = path.pushloc or path.loc
5618 dest = path.pushloc or path.loc
5616 branches = (path.branch, opts.get(b'branch') or [])
5619 branches = (path.branch, opts.get(b'branch') or [])
5617 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5620 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5618 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5621 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5619 other = hg.peer(repo, opts, dest)
5622 other = hg.peer(repo, opts, dest)
5620
5623
5621 if revs:
5624 if revs:
5622 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5625 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5623 if not revs:
5626 if not revs:
5624 raise error.Abort(
5627 raise error.Abort(
5625 _(b"specified revisions evaluate to an empty set"),
5628 _(b"specified revisions evaluate to an empty set"),
5626 hint=_(b"use different revision arguments"),
5629 hint=_(b"use different revision arguments"),
5627 )
5630 )
5628 elif path.pushrev:
5631 elif path.pushrev:
5629 # It doesn't make any sense to specify ancestor revisions. So limit
5632 # It doesn't make any sense to specify ancestor revisions. So limit
5630 # to DAG heads to make discovery simpler.
5633 # to DAG heads to make discovery simpler.
5631 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5634 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5632 revs = scmutil.revrange(repo, [expr])
5635 revs = scmutil.revrange(repo, [expr])
5633 revs = [repo[rev].node() for rev in revs]
5636 revs = [repo[rev].node() for rev in revs]
5634 if not revs:
5637 if not revs:
5635 raise error.Abort(
5638 raise error.Abort(
5636 _(b'default push revset for path evaluates to an empty set')
5639 _(b'default push revset for path evaluates to an empty set')
5637 )
5640 )
5638 elif ui.configbool(b'commands', b'push.require-revs'):
5641 elif ui.configbool(b'commands', b'push.require-revs'):
5639 raise error.Abort(
5642 raise error.Abort(
5640 _(b'no revisions specified to push'),
5643 _(b'no revisions specified to push'),
5641 hint=_(b'did you mean "hg push -r ."?'),
5644 hint=_(b'did you mean "hg push -r ."?'),
5642 )
5645 )
5643
5646
5644 repo._subtoppath = dest
5647 repo._subtoppath = dest
5645 try:
5648 try:
5646 # push subrepos depth-first for coherent ordering
5649 # push subrepos depth-first for coherent ordering
5647 c = repo[b'.']
5650 c = repo[b'.']
5648 subs = c.substate # only repos that are committed
5651 subs = c.substate # only repos that are committed
5649 for s in sorted(subs):
5652 for s in sorted(subs):
5650 result = c.sub(s).push(opts)
5653 result = c.sub(s).push(opts)
5651 if result == 0:
5654 if result == 0:
5652 return not result
5655 return not result
5653 finally:
5656 finally:
5654 del repo._subtoppath
5657 del repo._subtoppath
5655
5658
5656 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5659 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5657 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5660 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5658
5661
5659 pushop = exchange.push(
5662 pushop = exchange.push(
5660 repo,
5663 repo,
5661 other,
5664 other,
5662 opts.get(b'force'),
5665 opts.get(b'force'),
5663 revs=revs,
5666 revs=revs,
5664 newbranch=opts.get(b'new_branch'),
5667 newbranch=opts.get(b'new_branch'),
5665 bookmarks=opts.get(b'bookmark', ()),
5668 bookmarks=opts.get(b'bookmark', ()),
5666 publish=opts.get(b'publish'),
5669 publish=opts.get(b'publish'),
5667 opargs=opargs,
5670 opargs=opargs,
5668 )
5671 )
5669
5672
5670 result = not pushop.cgresult
5673 result = not pushop.cgresult
5671
5674
5672 if pushop.bkresult is not None:
5675 if pushop.bkresult is not None:
5673 if pushop.bkresult == 2:
5676 if pushop.bkresult == 2:
5674 result = 2
5677 result = 2
5675 elif not result and pushop.bkresult:
5678 elif not result and pushop.bkresult:
5676 result = 2
5679 result = 2
5677
5680
5678 return result
5681 return result
5679
5682
5680
5683
5681 @command(
5684 @command(
5682 b'recover',
5685 b'recover',
5683 [(b'', b'verify', True, b"run `hg verify` after succesful recover"),],
5686 [(b'', b'verify', True, b"run `hg verify` after succesful recover"),],
5684 helpcategory=command.CATEGORY_MAINTENANCE,
5687 helpcategory=command.CATEGORY_MAINTENANCE,
5685 )
5688 )
5686 def recover(ui, repo, **opts):
5689 def recover(ui, repo, **opts):
5687 """roll back an interrupted transaction
5690 """roll back an interrupted transaction
5688
5691
5689 Recover from an interrupted commit or pull.
5692 Recover from an interrupted commit or pull.
5690
5693
5691 This command tries to fix the repository status after an
5694 This command tries to fix the repository status after an
5692 interrupted operation. It should only be necessary when Mercurial
5695 interrupted operation. It should only be necessary when Mercurial
5693 suggests it.
5696 suggests it.
5694
5697
5695 Returns 0 if successful, 1 if nothing to recover or verify fails.
5698 Returns 0 if successful, 1 if nothing to recover or verify fails.
5696 """
5699 """
5697 ret = repo.recover()
5700 ret = repo.recover()
5698 if ret:
5701 if ret:
5699 if opts['verify']:
5702 if opts['verify']:
5700 return hg.verify(repo)
5703 return hg.verify(repo)
5701 else:
5704 else:
5702 msg = _(
5705 msg = _(
5703 b"(verify step skipped, run `hg verify` to check your "
5706 b"(verify step skipped, run `hg verify` to check your "
5704 b"repository content)\n"
5707 b"repository content)\n"
5705 )
5708 )
5706 ui.warn(msg)
5709 ui.warn(msg)
5707 return 0
5710 return 0
5708 return 1
5711 return 1
5709
5712
5710
5713
5711 @command(
5714 @command(
5712 b'remove|rm',
5715 b'remove|rm',
5713 [
5716 [
5714 (b'A', b'after', None, _(b'record delete for missing files')),
5717 (b'A', b'after', None, _(b'record delete for missing files')),
5715 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5718 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5716 ]
5719 ]
5717 + subrepoopts
5720 + subrepoopts
5718 + walkopts
5721 + walkopts
5719 + dryrunopts,
5722 + dryrunopts,
5720 _(b'[OPTION]... FILE...'),
5723 _(b'[OPTION]... FILE...'),
5721 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5724 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5722 helpbasic=True,
5725 helpbasic=True,
5723 inferrepo=True,
5726 inferrepo=True,
5724 )
5727 )
5725 def remove(ui, repo, *pats, **opts):
5728 def remove(ui, repo, *pats, **opts):
5726 """remove the specified files on the next commit
5729 """remove the specified files on the next commit
5727
5730
5728 Schedule the indicated files for removal from the current branch.
5731 Schedule the indicated files for removal from the current branch.
5729
5732
5730 This command schedules the files to be removed at the next commit.
5733 This command schedules the files to be removed at the next commit.
5731 To undo a remove before that, see :hg:`revert`. To undo added
5734 To undo a remove before that, see :hg:`revert`. To undo added
5732 files, see :hg:`forget`.
5735 files, see :hg:`forget`.
5733
5736
5734 .. container:: verbose
5737 .. container:: verbose
5735
5738
5736 -A/--after can be used to remove only files that have already
5739 -A/--after can be used to remove only files that have already
5737 been deleted, -f/--force can be used to force deletion, and -Af
5740 been deleted, -f/--force can be used to force deletion, and -Af
5738 can be used to remove files from the next revision without
5741 can be used to remove files from the next revision without
5739 deleting them from the working directory.
5742 deleting them from the working directory.
5740
5743
5741 The following table details the behavior of remove for different
5744 The following table details the behavior of remove for different
5742 file states (columns) and option combinations (rows). The file
5745 file states (columns) and option combinations (rows). The file
5743 states are Added [A], Clean [C], Modified [M] and Missing [!]
5746 states are Added [A], Clean [C], Modified [M] and Missing [!]
5744 (as reported by :hg:`status`). The actions are Warn, Remove
5747 (as reported by :hg:`status`). The actions are Warn, Remove
5745 (from branch) and Delete (from disk):
5748 (from branch) and Delete (from disk):
5746
5749
5747 ========= == == == ==
5750 ========= == == == ==
5748 opt/state A C M !
5751 opt/state A C M !
5749 ========= == == == ==
5752 ========= == == == ==
5750 none W RD W R
5753 none W RD W R
5751 -f R RD RD R
5754 -f R RD RD R
5752 -A W W W R
5755 -A W W W R
5753 -Af R R R R
5756 -Af R R R R
5754 ========= == == == ==
5757 ========= == == == ==
5755
5758
5756 .. note::
5759 .. note::
5757
5760
5758 :hg:`remove` never deletes files in Added [A] state from the
5761 :hg:`remove` never deletes files in Added [A] state from the
5759 working directory, not even if ``--force`` is specified.
5762 working directory, not even if ``--force`` is specified.
5760
5763
5761 Returns 0 on success, 1 if any warnings encountered.
5764 Returns 0 on success, 1 if any warnings encountered.
5762 """
5765 """
5763
5766
5764 opts = pycompat.byteskwargs(opts)
5767 opts = pycompat.byteskwargs(opts)
5765 after, force = opts.get(b'after'), opts.get(b'force')
5768 after, force = opts.get(b'after'), opts.get(b'force')
5766 dryrun = opts.get(b'dry_run')
5769 dryrun = opts.get(b'dry_run')
5767 if not pats and not after:
5770 if not pats and not after:
5768 raise error.Abort(_(b'no files specified'))
5771 raise error.Abort(_(b'no files specified'))
5769
5772
5770 m = scmutil.match(repo[None], pats, opts)
5773 m = scmutil.match(repo[None], pats, opts)
5771 subrepos = opts.get(b'subrepos')
5774 subrepos = opts.get(b'subrepos')
5772 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5775 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5773 return cmdutil.remove(
5776 return cmdutil.remove(
5774 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5777 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5775 )
5778 )
5776
5779
5777
5780
5778 @command(
5781 @command(
5779 b'rename|move|mv',
5782 b'rename|move|mv',
5780 [
5783 [
5781 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5784 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5782 (
5785 (
5783 b'f',
5786 b'f',
5784 b'force',
5787 b'force',
5785 None,
5788 None,
5786 _(b'forcibly move over an existing managed file'),
5789 _(b'forcibly move over an existing managed file'),
5787 ),
5790 ),
5788 ]
5791 ]
5789 + walkopts
5792 + walkopts
5790 + dryrunopts,
5793 + dryrunopts,
5791 _(b'[OPTION]... SOURCE... DEST'),
5794 _(b'[OPTION]... SOURCE... DEST'),
5792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5795 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5793 )
5796 )
5794 def rename(ui, repo, *pats, **opts):
5797 def rename(ui, repo, *pats, **opts):
5795 """rename files; equivalent of copy + remove
5798 """rename files; equivalent of copy + remove
5796
5799
5797 Mark dest as copies of sources; mark sources for deletion. If dest
5800 Mark dest as copies of sources; mark sources for deletion. If dest
5798 is a directory, copies are put in that directory. If dest is a
5801 is a directory, copies are put in that directory. If dest is a
5799 file, there can only be one source.
5802 file, there can only be one source.
5800
5803
5801 By default, this command copies the contents of files as they
5804 By default, this command copies the contents of files as they
5802 exist in the working directory. If invoked with -A/--after, the
5805 exist in the working directory. If invoked with -A/--after, the
5803 operation is recorded, but no copying is performed.
5806 operation is recorded, but no copying is performed.
5804
5807
5805 This command takes effect at the next commit. To undo a rename
5808 This command takes effect at the next commit. To undo a rename
5806 before that, see :hg:`revert`.
5809 before that, see :hg:`revert`.
5807
5810
5808 Returns 0 on success, 1 if errors are encountered.
5811 Returns 0 on success, 1 if errors are encountered.
5809 """
5812 """
5810 opts = pycompat.byteskwargs(opts)
5813 opts = pycompat.byteskwargs(opts)
5811 with repo.wlock(False):
5814 with repo.wlock(False):
5812 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5815 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5813
5816
5814
5817
5815 @command(
5818 @command(
5816 b'resolve',
5819 b'resolve',
5817 [
5820 [
5818 (b'a', b'all', None, _(b'select all unresolved files')),
5821 (b'a', b'all', None, _(b'select all unresolved files')),
5819 (b'l', b'list', None, _(b'list state of files needing merge')),
5822 (b'l', b'list', None, _(b'list state of files needing merge')),
5820 (b'm', b'mark', None, _(b'mark files as resolved')),
5823 (b'm', b'mark', None, _(b'mark files as resolved')),
5821 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5824 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5822 (b'n', b'no-status', None, _(b'hide status prefix')),
5825 (b'n', b'no-status', None, _(b'hide status prefix')),
5823 (b'', b're-merge', None, _(b're-merge files')),
5826 (b'', b're-merge', None, _(b're-merge files')),
5824 ]
5827 ]
5825 + mergetoolopts
5828 + mergetoolopts
5826 + walkopts
5829 + walkopts
5827 + formatteropts,
5830 + formatteropts,
5828 _(b'[OPTION]... [FILE]...'),
5831 _(b'[OPTION]... [FILE]...'),
5829 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5832 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5830 inferrepo=True,
5833 inferrepo=True,
5831 )
5834 )
5832 def resolve(ui, repo, *pats, **opts):
5835 def resolve(ui, repo, *pats, **opts):
5833 """redo merges or set/view the merge status of files
5836 """redo merges or set/view the merge status of files
5834
5837
5835 Merges with unresolved conflicts are often the result of
5838 Merges with unresolved conflicts are often the result of
5836 non-interactive merging using the ``internal:merge`` configuration
5839 non-interactive merging using the ``internal:merge`` configuration
5837 setting, or a command-line merge tool like ``diff3``. The resolve
5840 setting, or a command-line merge tool like ``diff3``. The resolve
5838 command is used to manage the files involved in a merge, after
5841 command is used to manage the files involved in a merge, after
5839 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5842 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5840 working directory must have two parents). See :hg:`help
5843 working directory must have two parents). See :hg:`help
5841 merge-tools` for information on configuring merge tools.
5844 merge-tools` for information on configuring merge tools.
5842
5845
5843 The resolve command can be used in the following ways:
5846 The resolve command can be used in the following ways:
5844
5847
5845 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5848 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5846 the specified files, discarding any previous merge attempts. Re-merging
5849 the specified files, discarding any previous merge attempts. Re-merging
5847 is not performed for files already marked as resolved. Use ``--all/-a``
5850 is not performed for files already marked as resolved. Use ``--all/-a``
5848 to select all unresolved files. ``--tool`` can be used to specify
5851 to select all unresolved files. ``--tool`` can be used to specify
5849 the merge tool used for the given files. It overrides the HGMERGE
5852 the merge tool used for the given files. It overrides the HGMERGE
5850 environment variable and your configuration files. Previous file
5853 environment variable and your configuration files. Previous file
5851 contents are saved with a ``.orig`` suffix.
5854 contents are saved with a ``.orig`` suffix.
5852
5855
5853 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5856 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5854 (e.g. after having manually fixed-up the files). The default is
5857 (e.g. after having manually fixed-up the files). The default is
5855 to mark all unresolved files.
5858 to mark all unresolved files.
5856
5859
5857 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5860 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5858 default is to mark all resolved files.
5861 default is to mark all resolved files.
5859
5862
5860 - :hg:`resolve -l`: list files which had or still have conflicts.
5863 - :hg:`resolve -l`: list files which had or still have conflicts.
5861 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5864 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5862 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5865 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5863 the list. See :hg:`help filesets` for details.
5866 the list. See :hg:`help filesets` for details.
5864
5867
5865 .. note::
5868 .. note::
5866
5869
5867 Mercurial will not let you commit files with unresolved merge
5870 Mercurial will not let you commit files with unresolved merge
5868 conflicts. You must use :hg:`resolve -m ...` before you can
5871 conflicts. You must use :hg:`resolve -m ...` before you can
5869 commit after a conflicting merge.
5872 commit after a conflicting merge.
5870
5873
5871 .. container:: verbose
5874 .. container:: verbose
5872
5875
5873 Template:
5876 Template:
5874
5877
5875 The following keywords are supported in addition to the common template
5878 The following keywords are supported in addition to the common template
5876 keywords and functions. See also :hg:`help templates`.
5879 keywords and functions. See also :hg:`help templates`.
5877
5880
5878 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5881 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5879 :path: String. Repository-absolute path of the file.
5882 :path: String. Repository-absolute path of the file.
5880
5883
5881 Returns 0 on success, 1 if any files fail a resolve attempt.
5884 Returns 0 on success, 1 if any files fail a resolve attempt.
5882 """
5885 """
5883
5886
5884 opts = pycompat.byteskwargs(opts)
5887 opts = pycompat.byteskwargs(opts)
5885 confirm = ui.configbool(b'commands', b'resolve.confirm')
5888 confirm = ui.configbool(b'commands', b'resolve.confirm')
5886 flaglist = b'all mark unmark list no_status re_merge'.split()
5889 flaglist = b'all mark unmark list no_status re_merge'.split()
5887 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5890 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5888
5891
5889 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5892 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5890 if actioncount > 1:
5893 if actioncount > 1:
5891 raise error.Abort(_(b"too many actions specified"))
5894 raise error.Abort(_(b"too many actions specified"))
5892 elif actioncount == 0 and ui.configbool(
5895 elif actioncount == 0 and ui.configbool(
5893 b'commands', b'resolve.explicit-re-merge'
5896 b'commands', b'resolve.explicit-re-merge'
5894 ):
5897 ):
5895 hint = _(b'use --mark, --unmark, --list or --re-merge')
5898 hint = _(b'use --mark, --unmark, --list or --re-merge')
5896 raise error.Abort(_(b'no action specified'), hint=hint)
5899 raise error.Abort(_(b'no action specified'), hint=hint)
5897 if pats and all:
5900 if pats and all:
5898 raise error.Abort(_(b"can't specify --all and patterns"))
5901 raise error.Abort(_(b"can't specify --all and patterns"))
5899 if not (all or pats or show or mark or unmark):
5902 if not (all or pats or show or mark or unmark):
5900 raise error.Abort(
5903 raise error.Abort(
5901 _(b'no files or directories specified'),
5904 _(b'no files or directories specified'),
5902 hint=b'use --all to re-merge all unresolved files',
5905 hint=b'use --all to re-merge all unresolved files',
5903 )
5906 )
5904
5907
5905 if confirm:
5908 if confirm:
5906 if all:
5909 if all:
5907 if ui.promptchoice(
5910 if ui.promptchoice(
5908 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5911 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5909 ):
5912 ):
5910 raise error.Abort(_(b'user quit'))
5913 raise error.Abort(_(b'user quit'))
5911 if mark and not pats:
5914 if mark and not pats:
5912 if ui.promptchoice(
5915 if ui.promptchoice(
5913 _(
5916 _(
5914 b'mark all unresolved files as resolved (yn)?'
5917 b'mark all unresolved files as resolved (yn)?'
5915 b'$$ &Yes $$ &No'
5918 b'$$ &Yes $$ &No'
5916 )
5919 )
5917 ):
5920 ):
5918 raise error.Abort(_(b'user quit'))
5921 raise error.Abort(_(b'user quit'))
5919 if unmark and not pats:
5922 if unmark and not pats:
5920 if ui.promptchoice(
5923 if ui.promptchoice(
5921 _(
5924 _(
5922 b'mark all resolved files as unresolved (yn)?'
5925 b'mark all resolved files as unresolved (yn)?'
5923 b'$$ &Yes $$ &No'
5926 b'$$ &Yes $$ &No'
5924 )
5927 )
5925 ):
5928 ):
5926 raise error.Abort(_(b'user quit'))
5929 raise error.Abort(_(b'user quit'))
5927
5930
5928 uipathfn = scmutil.getuipathfn(repo)
5931 uipathfn = scmutil.getuipathfn(repo)
5929
5932
5930 if show:
5933 if show:
5931 ui.pager(b'resolve')
5934 ui.pager(b'resolve')
5932 fm = ui.formatter(b'resolve', opts)
5935 fm = ui.formatter(b'resolve', opts)
5933 ms = mergemod.mergestate.read(repo)
5936 ms = mergemod.mergestate.read(repo)
5934 wctx = repo[None]
5937 wctx = repo[None]
5935 m = scmutil.match(wctx, pats, opts)
5938 m = scmutil.match(wctx, pats, opts)
5936
5939
5937 # Labels and keys based on merge state. Unresolved path conflicts show
5940 # Labels and keys based on merge state. Unresolved path conflicts show
5938 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5941 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5939 # resolved conflicts.
5942 # resolved conflicts.
5940 mergestateinfo = {
5943 mergestateinfo = {
5941 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5944 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5942 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5945 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5943 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5946 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5944 b'resolve.unresolved',
5947 b'resolve.unresolved',
5945 b'P',
5948 b'P',
5946 ),
5949 ),
5947 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5950 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5948 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5951 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5949 b'resolve.driverresolved',
5952 b'resolve.driverresolved',
5950 b'D',
5953 b'D',
5951 ),
5954 ),
5952 }
5955 }
5953
5956
5954 for f in ms:
5957 for f in ms:
5955 if not m(f):
5958 if not m(f):
5956 continue
5959 continue
5957
5960
5958 label, key = mergestateinfo[ms[f]]
5961 label, key = mergestateinfo[ms[f]]
5959 fm.startitem()
5962 fm.startitem()
5960 fm.context(ctx=wctx)
5963 fm.context(ctx=wctx)
5961 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5964 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5962 fm.data(path=f)
5965 fm.data(path=f)
5963 fm.plain(b'%s\n' % uipathfn(f), label=label)
5966 fm.plain(b'%s\n' % uipathfn(f), label=label)
5964 fm.end()
5967 fm.end()
5965 return 0
5968 return 0
5966
5969
5967 with repo.wlock():
5970 with repo.wlock():
5968 ms = mergemod.mergestate.read(repo)
5971 ms = mergemod.mergestate.read(repo)
5969
5972
5970 if not (ms.active() or repo.dirstate.p2() != nullid):
5973 if not (ms.active() or repo.dirstate.p2() != nullid):
5971 raise error.Abort(
5974 raise error.Abort(
5972 _(b'resolve command not applicable when not merging')
5975 _(b'resolve command not applicable when not merging')
5973 )
5976 )
5974
5977
5975 wctx = repo[None]
5978 wctx = repo[None]
5976
5979
5977 if (
5980 if (
5978 ms.mergedriver
5981 ms.mergedriver
5979 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5982 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5980 ):
5983 ):
5981 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5984 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5982 ms.commit()
5985 ms.commit()
5983 # allow mark and unmark to go through
5986 # allow mark and unmark to go through
5984 if not mark and not unmark and not proceed:
5987 if not mark and not unmark and not proceed:
5985 return 1
5988 return 1
5986
5989
5987 m = scmutil.match(wctx, pats, opts)
5990 m = scmutil.match(wctx, pats, opts)
5988 ret = 0
5991 ret = 0
5989 didwork = False
5992 didwork = False
5990 runconclude = False
5993 runconclude = False
5991
5994
5992 tocomplete = []
5995 tocomplete = []
5993 hasconflictmarkers = []
5996 hasconflictmarkers = []
5994 if mark:
5997 if mark:
5995 markcheck = ui.config(b'commands', b'resolve.mark-check')
5998 markcheck = ui.config(b'commands', b'resolve.mark-check')
5996 if markcheck not in [b'warn', b'abort']:
5999 if markcheck not in [b'warn', b'abort']:
5997 # Treat all invalid / unrecognized values as 'none'.
6000 # Treat all invalid / unrecognized values as 'none'.
5998 markcheck = False
6001 markcheck = False
5999 for f in ms:
6002 for f in ms:
6000 if not m(f):
6003 if not m(f):
6001 continue
6004 continue
6002
6005
6003 didwork = True
6006 didwork = True
6004
6007
6005 # don't let driver-resolved files be marked, and run the conclude
6008 # don't let driver-resolved files be marked, and run the conclude
6006 # step if asked to resolve
6009 # step if asked to resolve
6007 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6010 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6008 exact = m.exact(f)
6011 exact = m.exact(f)
6009 if mark:
6012 if mark:
6010 if exact:
6013 if exact:
6011 ui.warn(
6014 ui.warn(
6012 _(b'not marking %s as it is driver-resolved\n')
6015 _(b'not marking %s as it is driver-resolved\n')
6013 % uipathfn(f)
6016 % uipathfn(f)
6014 )
6017 )
6015 elif unmark:
6018 elif unmark:
6016 if exact:
6019 if exact:
6017 ui.warn(
6020 ui.warn(
6018 _(b'not unmarking %s as it is driver-resolved\n')
6021 _(b'not unmarking %s as it is driver-resolved\n')
6019 % uipathfn(f)
6022 % uipathfn(f)
6020 )
6023 )
6021 else:
6024 else:
6022 runconclude = True
6025 runconclude = True
6023 continue
6026 continue
6024
6027
6025 # path conflicts must be resolved manually
6028 # path conflicts must be resolved manually
6026 if ms[f] in (
6029 if ms[f] in (
6027 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6030 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6028 mergemod.MERGE_RECORD_RESOLVED_PATH,
6031 mergemod.MERGE_RECORD_RESOLVED_PATH,
6029 ):
6032 ):
6030 if mark:
6033 if mark:
6031 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6034 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6032 elif unmark:
6035 elif unmark:
6033 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6036 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6034 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6037 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6035 ui.warn(
6038 ui.warn(
6036 _(b'%s: path conflict must be resolved manually\n')
6039 _(b'%s: path conflict must be resolved manually\n')
6037 % uipathfn(f)
6040 % uipathfn(f)
6038 )
6041 )
6039 continue
6042 continue
6040
6043
6041 if mark:
6044 if mark:
6042 if markcheck:
6045 if markcheck:
6043 fdata = repo.wvfs.tryread(f)
6046 fdata = repo.wvfs.tryread(f)
6044 if (
6047 if (
6045 filemerge.hasconflictmarkers(fdata)
6048 filemerge.hasconflictmarkers(fdata)
6046 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6049 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6047 ):
6050 ):
6048 hasconflictmarkers.append(f)
6051 hasconflictmarkers.append(f)
6049 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6052 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6050 elif unmark:
6053 elif unmark:
6051 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6054 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6052 else:
6055 else:
6053 # backup pre-resolve (merge uses .orig for its own purposes)
6056 # backup pre-resolve (merge uses .orig for its own purposes)
6054 a = repo.wjoin(f)
6057 a = repo.wjoin(f)
6055 try:
6058 try:
6056 util.copyfile(a, a + b".resolve")
6059 util.copyfile(a, a + b".resolve")
6057 except (IOError, OSError) as inst:
6060 except (IOError, OSError) as inst:
6058 if inst.errno != errno.ENOENT:
6061 if inst.errno != errno.ENOENT:
6059 raise
6062 raise
6060
6063
6061 try:
6064 try:
6062 # preresolve file
6065 # preresolve file
6063 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6066 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6064 with ui.configoverride(overrides, b'resolve'):
6067 with ui.configoverride(overrides, b'resolve'):
6065 complete, r = ms.preresolve(f, wctx)
6068 complete, r = ms.preresolve(f, wctx)
6066 if not complete:
6069 if not complete:
6067 tocomplete.append(f)
6070 tocomplete.append(f)
6068 elif r:
6071 elif r:
6069 ret = 1
6072 ret = 1
6070 finally:
6073 finally:
6071 ms.commit()
6074 ms.commit()
6072
6075
6073 # replace filemerge's .orig file with our resolve file, but only
6076 # replace filemerge's .orig file with our resolve file, but only
6074 # for merges that are complete
6077 # for merges that are complete
6075 if complete:
6078 if complete:
6076 try:
6079 try:
6077 util.rename(
6080 util.rename(
6078 a + b".resolve", scmutil.backuppath(ui, repo, f)
6081 a + b".resolve", scmutil.backuppath(ui, repo, f)
6079 )
6082 )
6080 except OSError as inst:
6083 except OSError as inst:
6081 if inst.errno != errno.ENOENT:
6084 if inst.errno != errno.ENOENT:
6082 raise
6085 raise
6083
6086
6084 if hasconflictmarkers:
6087 if hasconflictmarkers:
6085 ui.warn(
6088 ui.warn(
6086 _(
6089 _(
6087 b'warning: the following files still have conflict '
6090 b'warning: the following files still have conflict '
6088 b'markers:\n'
6091 b'markers:\n'
6089 )
6092 )
6090 + b''.join(
6093 + b''.join(
6091 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6094 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6092 )
6095 )
6093 )
6096 )
6094 if markcheck == b'abort' and not all and not pats:
6097 if markcheck == b'abort' and not all and not pats:
6095 raise error.Abort(
6098 raise error.Abort(
6096 _(b'conflict markers detected'),
6099 _(b'conflict markers detected'),
6097 hint=_(b'use --all to mark anyway'),
6100 hint=_(b'use --all to mark anyway'),
6098 )
6101 )
6099
6102
6100 for f in tocomplete:
6103 for f in tocomplete:
6101 try:
6104 try:
6102 # resolve file
6105 # resolve file
6103 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6106 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6104 with ui.configoverride(overrides, b'resolve'):
6107 with ui.configoverride(overrides, b'resolve'):
6105 r = ms.resolve(f, wctx)
6108 r = ms.resolve(f, wctx)
6106 if r:
6109 if r:
6107 ret = 1
6110 ret = 1
6108 finally:
6111 finally:
6109 ms.commit()
6112 ms.commit()
6110
6113
6111 # replace filemerge's .orig file with our resolve file
6114 # replace filemerge's .orig file with our resolve file
6112 a = repo.wjoin(f)
6115 a = repo.wjoin(f)
6113 try:
6116 try:
6114 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6117 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6115 except OSError as inst:
6118 except OSError as inst:
6116 if inst.errno != errno.ENOENT:
6119 if inst.errno != errno.ENOENT:
6117 raise
6120 raise
6118
6121
6119 ms.commit()
6122 ms.commit()
6120 ms.recordactions()
6123 ms.recordactions()
6121
6124
6122 if not didwork and pats:
6125 if not didwork and pats:
6123 hint = None
6126 hint = None
6124 if not any([p for p in pats if p.find(b':') >= 0]):
6127 if not any([p for p in pats if p.find(b':') >= 0]):
6125 pats = [b'path:%s' % p for p in pats]
6128 pats = [b'path:%s' % p for p in pats]
6126 m = scmutil.match(wctx, pats, opts)
6129 m = scmutil.match(wctx, pats, opts)
6127 for f in ms:
6130 for f in ms:
6128 if not m(f):
6131 if not m(f):
6129 continue
6132 continue
6130
6133
6131 def flag(o):
6134 def flag(o):
6132 if o == b're_merge':
6135 if o == b're_merge':
6133 return b'--re-merge '
6136 return b'--re-merge '
6134 return b'-%s ' % o[0:1]
6137 return b'-%s ' % o[0:1]
6135
6138
6136 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6139 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6137 hint = _(b"(try: hg resolve %s%s)\n") % (
6140 hint = _(b"(try: hg resolve %s%s)\n") % (
6138 flags,
6141 flags,
6139 b' '.join(pats),
6142 b' '.join(pats),
6140 )
6143 )
6141 break
6144 break
6142 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6145 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6143 if hint:
6146 if hint:
6144 ui.warn(hint)
6147 ui.warn(hint)
6145 elif ms.mergedriver and ms.mdstate() != b's':
6148 elif ms.mergedriver and ms.mdstate() != b's':
6146 # run conclude step when either a driver-resolved file is requested
6149 # run conclude step when either a driver-resolved file is requested
6147 # or there are no driver-resolved files
6150 # or there are no driver-resolved files
6148 # we can't use 'ret' to determine whether any files are unresolved
6151 # we can't use 'ret' to determine whether any files are unresolved
6149 # because we might not have tried to resolve some
6152 # because we might not have tried to resolve some
6150 if (runconclude or not list(ms.driverresolved())) and not list(
6153 if (runconclude or not list(ms.driverresolved())) and not list(
6151 ms.unresolved()
6154 ms.unresolved()
6152 ):
6155 ):
6153 proceed = mergemod.driverconclude(repo, ms, wctx)
6156 proceed = mergemod.driverconclude(repo, ms, wctx)
6154 ms.commit()
6157 ms.commit()
6155 if not proceed:
6158 if not proceed:
6156 return 1
6159 return 1
6157
6160
6158 # Nudge users into finishing an unfinished operation
6161 # Nudge users into finishing an unfinished operation
6159 unresolvedf = list(ms.unresolved())
6162 unresolvedf = list(ms.unresolved())
6160 driverresolvedf = list(ms.driverresolved())
6163 driverresolvedf = list(ms.driverresolved())
6161 if not unresolvedf and not driverresolvedf:
6164 if not unresolvedf and not driverresolvedf:
6162 ui.status(_(b'(no more unresolved files)\n'))
6165 ui.status(_(b'(no more unresolved files)\n'))
6163 cmdutil.checkafterresolved(repo)
6166 cmdutil.checkafterresolved(repo)
6164 elif not unresolvedf:
6167 elif not unresolvedf:
6165 ui.status(
6168 ui.status(
6166 _(
6169 _(
6167 b'(no more unresolved files -- '
6170 b'(no more unresolved files -- '
6168 b'run "hg resolve --all" to conclude)\n'
6171 b'run "hg resolve --all" to conclude)\n'
6169 )
6172 )
6170 )
6173 )
6171
6174
6172 return ret
6175 return ret
6173
6176
6174
6177
6175 @command(
6178 @command(
6176 b'revert',
6179 b'revert',
6177 [
6180 [
6178 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6181 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6179 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6182 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6180 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6183 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6181 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6184 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6182 (b'i', b'interactive', None, _(b'interactively select the changes')),
6185 (b'i', b'interactive', None, _(b'interactively select the changes')),
6183 ]
6186 ]
6184 + walkopts
6187 + walkopts
6185 + dryrunopts,
6188 + dryrunopts,
6186 _(b'[OPTION]... [-r REV] [NAME]...'),
6189 _(b'[OPTION]... [-r REV] [NAME]...'),
6187 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6190 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6188 )
6191 )
6189 def revert(ui, repo, *pats, **opts):
6192 def revert(ui, repo, *pats, **opts):
6190 """restore files to their checkout state
6193 """restore files to their checkout state
6191
6194
6192 .. note::
6195 .. note::
6193
6196
6194 To check out earlier revisions, you should use :hg:`update REV`.
6197 To check out earlier revisions, you should use :hg:`update REV`.
6195 To cancel an uncommitted merge (and lose your changes),
6198 To cancel an uncommitted merge (and lose your changes),
6196 use :hg:`merge --abort`.
6199 use :hg:`merge --abort`.
6197
6200
6198 With no revision specified, revert the specified files or directories
6201 With no revision specified, revert the specified files or directories
6199 to the contents they had in the parent of the working directory.
6202 to the contents they had in the parent of the working directory.
6200 This restores the contents of files to an unmodified
6203 This restores the contents of files to an unmodified
6201 state and unschedules adds, removes, copies, and renames. If the
6204 state and unschedules adds, removes, copies, and renames. If the
6202 working directory has two parents, you must explicitly specify a
6205 working directory has two parents, you must explicitly specify a
6203 revision.
6206 revision.
6204
6207
6205 Using the -r/--rev or -d/--date options, revert the given files or
6208 Using the -r/--rev or -d/--date options, revert the given files or
6206 directories to their states as of a specific revision. Because
6209 directories to their states as of a specific revision. Because
6207 revert does not change the working directory parents, this will
6210 revert does not change the working directory parents, this will
6208 cause these files to appear modified. This can be helpful to "back
6211 cause these files to appear modified. This can be helpful to "back
6209 out" some or all of an earlier change. See :hg:`backout` for a
6212 out" some or all of an earlier change. See :hg:`backout` for a
6210 related method.
6213 related method.
6211
6214
6212 Modified files are saved with a .orig suffix before reverting.
6215 Modified files are saved with a .orig suffix before reverting.
6213 To disable these backups, use --no-backup. It is possible to store
6216 To disable these backups, use --no-backup. It is possible to store
6214 the backup files in a custom directory relative to the root of the
6217 the backup files in a custom directory relative to the root of the
6215 repository by setting the ``ui.origbackuppath`` configuration
6218 repository by setting the ``ui.origbackuppath`` configuration
6216 option.
6219 option.
6217
6220
6218 See :hg:`help dates` for a list of formats valid for -d/--date.
6221 See :hg:`help dates` for a list of formats valid for -d/--date.
6219
6222
6220 See :hg:`help backout` for a way to reverse the effect of an
6223 See :hg:`help backout` for a way to reverse the effect of an
6221 earlier changeset.
6224 earlier changeset.
6222
6225
6223 Returns 0 on success.
6226 Returns 0 on success.
6224 """
6227 """
6225
6228
6226 opts = pycompat.byteskwargs(opts)
6229 opts = pycompat.byteskwargs(opts)
6227 if opts.get(b"date"):
6230 if opts.get(b"date"):
6228 if opts.get(b"rev"):
6231 if opts.get(b"rev"):
6229 raise error.Abort(_(b"you can't specify a revision and a date"))
6232 raise error.Abort(_(b"you can't specify a revision and a date"))
6230 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6233 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6231
6234
6232 parent, p2 = repo.dirstate.parents()
6235 parent, p2 = repo.dirstate.parents()
6233 if not opts.get(b'rev') and p2 != nullid:
6236 if not opts.get(b'rev') and p2 != nullid:
6234 # revert after merge is a trap for new users (issue2915)
6237 # revert after merge is a trap for new users (issue2915)
6235 raise error.Abort(
6238 raise error.Abort(
6236 _(b'uncommitted merge with no revision specified'),
6239 _(b'uncommitted merge with no revision specified'),
6237 hint=_(b"use 'hg update' or see 'hg help revert'"),
6240 hint=_(b"use 'hg update' or see 'hg help revert'"),
6238 )
6241 )
6239
6242
6240 rev = opts.get(b'rev')
6243 rev = opts.get(b'rev')
6241 if rev:
6244 if rev:
6242 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6245 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6243 ctx = scmutil.revsingle(repo, rev)
6246 ctx = scmutil.revsingle(repo, rev)
6244
6247
6245 if not (
6248 if not (
6246 pats
6249 pats
6247 or opts.get(b'include')
6250 or opts.get(b'include')
6248 or opts.get(b'exclude')
6251 or opts.get(b'exclude')
6249 or opts.get(b'all')
6252 or opts.get(b'all')
6250 or opts.get(b'interactive')
6253 or opts.get(b'interactive')
6251 ):
6254 ):
6252 msg = _(b"no files or directories specified")
6255 msg = _(b"no files or directories specified")
6253 if p2 != nullid:
6256 if p2 != nullid:
6254 hint = _(
6257 hint = _(
6255 b"uncommitted merge, use --all to discard all changes,"
6258 b"uncommitted merge, use --all to discard all changes,"
6256 b" or 'hg update -C .' to abort the merge"
6259 b" or 'hg update -C .' to abort the merge"
6257 )
6260 )
6258 raise error.Abort(msg, hint=hint)
6261 raise error.Abort(msg, hint=hint)
6259 dirty = any(repo.status())
6262 dirty = any(repo.status())
6260 node = ctx.node()
6263 node = ctx.node()
6261 if node != parent:
6264 if node != parent:
6262 if dirty:
6265 if dirty:
6263 hint = (
6266 hint = (
6264 _(
6267 _(
6265 b"uncommitted changes, use --all to discard all"
6268 b"uncommitted changes, use --all to discard all"
6266 b" changes, or 'hg update %d' to update"
6269 b" changes, or 'hg update %d' to update"
6267 )
6270 )
6268 % ctx.rev()
6271 % ctx.rev()
6269 )
6272 )
6270 else:
6273 else:
6271 hint = (
6274 hint = (
6272 _(
6275 _(
6273 b"use --all to revert all files,"
6276 b"use --all to revert all files,"
6274 b" or 'hg update %d' to update"
6277 b" or 'hg update %d' to update"
6275 )
6278 )
6276 % ctx.rev()
6279 % ctx.rev()
6277 )
6280 )
6278 elif dirty:
6281 elif dirty:
6279 hint = _(b"uncommitted changes, use --all to discard all changes")
6282 hint = _(b"uncommitted changes, use --all to discard all changes")
6280 else:
6283 else:
6281 hint = _(b"use --all to revert all files")
6284 hint = _(b"use --all to revert all files")
6282 raise error.Abort(msg, hint=hint)
6285 raise error.Abort(msg, hint=hint)
6283
6286
6284 return cmdutil.revert(
6287 return cmdutil.revert(
6285 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6288 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6286 )
6289 )
6287
6290
6288
6291
6289 @command(
6292 @command(
6290 b'rollback',
6293 b'rollback',
6291 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6294 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6292 helpcategory=command.CATEGORY_MAINTENANCE,
6295 helpcategory=command.CATEGORY_MAINTENANCE,
6293 )
6296 )
6294 def rollback(ui, repo, **opts):
6297 def rollback(ui, repo, **opts):
6295 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6298 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6296
6299
6297 Please use :hg:`commit --amend` instead of rollback to correct
6300 Please use :hg:`commit --amend` instead of rollback to correct
6298 mistakes in the last commit.
6301 mistakes in the last commit.
6299
6302
6300 This command should be used with care. There is only one level of
6303 This command should be used with care. There is only one level of
6301 rollback, and there is no way to undo a rollback. It will also
6304 rollback, and there is no way to undo a rollback. It will also
6302 restore the dirstate at the time of the last transaction, losing
6305 restore the dirstate at the time of the last transaction, losing
6303 any dirstate changes since that time. This command does not alter
6306 any dirstate changes since that time. This command does not alter
6304 the working directory.
6307 the working directory.
6305
6308
6306 Transactions are used to encapsulate the effects of all commands
6309 Transactions are used to encapsulate the effects of all commands
6307 that create new changesets or propagate existing changesets into a
6310 that create new changesets or propagate existing changesets into a
6308 repository.
6311 repository.
6309
6312
6310 .. container:: verbose
6313 .. container:: verbose
6311
6314
6312 For example, the following commands are transactional, and their
6315 For example, the following commands are transactional, and their
6313 effects can be rolled back:
6316 effects can be rolled back:
6314
6317
6315 - commit
6318 - commit
6316 - import
6319 - import
6317 - pull
6320 - pull
6318 - push (with this repository as the destination)
6321 - push (with this repository as the destination)
6319 - unbundle
6322 - unbundle
6320
6323
6321 To avoid permanent data loss, rollback will refuse to rollback a
6324 To avoid permanent data loss, rollback will refuse to rollback a
6322 commit transaction if it isn't checked out. Use --force to
6325 commit transaction if it isn't checked out. Use --force to
6323 override this protection.
6326 override this protection.
6324
6327
6325 The rollback command can be entirely disabled by setting the
6328 The rollback command can be entirely disabled by setting the
6326 ``ui.rollback`` configuration setting to false. If you're here
6329 ``ui.rollback`` configuration setting to false. If you're here
6327 because you want to use rollback and it's disabled, you can
6330 because you want to use rollback and it's disabled, you can
6328 re-enable the command by setting ``ui.rollback`` to true.
6331 re-enable the command by setting ``ui.rollback`` to true.
6329
6332
6330 This command is not intended for use on public repositories. Once
6333 This command is not intended for use on public repositories. Once
6331 changes are visible for pull by other users, rolling a transaction
6334 changes are visible for pull by other users, rolling a transaction
6332 back locally is ineffective (someone else may already have pulled
6335 back locally is ineffective (someone else may already have pulled
6333 the changes). Furthermore, a race is possible with readers of the
6336 the changes). Furthermore, a race is possible with readers of the
6334 repository; for example an in-progress pull from the repository
6337 repository; for example an in-progress pull from the repository
6335 may fail if a rollback is performed.
6338 may fail if a rollback is performed.
6336
6339
6337 Returns 0 on success, 1 if no rollback data is available.
6340 Returns 0 on success, 1 if no rollback data is available.
6338 """
6341 """
6339 if not ui.configbool(b'ui', b'rollback'):
6342 if not ui.configbool(b'ui', b'rollback'):
6340 raise error.Abort(
6343 raise error.Abort(
6341 _(b'rollback is disabled because it is unsafe'),
6344 _(b'rollback is disabled because it is unsafe'),
6342 hint=b'see `hg help -v rollback` for information',
6345 hint=b'see `hg help -v rollback` for information',
6343 )
6346 )
6344 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6347 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6345
6348
6346
6349
6347 @command(
6350 @command(
6348 b'root',
6351 b'root',
6349 [] + formatteropts,
6352 [] + formatteropts,
6350 intents={INTENT_READONLY},
6353 intents={INTENT_READONLY},
6351 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6354 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6352 )
6355 )
6353 def root(ui, repo, **opts):
6356 def root(ui, repo, **opts):
6354 """print the root (top) of the current working directory
6357 """print the root (top) of the current working directory
6355
6358
6356 Print the root directory of the current repository.
6359 Print the root directory of the current repository.
6357
6360
6358 .. container:: verbose
6361 .. container:: verbose
6359
6362
6360 Template:
6363 Template:
6361
6364
6362 The following keywords are supported in addition to the common template
6365 The following keywords are supported in addition to the common template
6363 keywords and functions. See also :hg:`help templates`.
6366 keywords and functions. See also :hg:`help templates`.
6364
6367
6365 :hgpath: String. Path to the .hg directory.
6368 :hgpath: String. Path to the .hg directory.
6366 :storepath: String. Path to the directory holding versioned data.
6369 :storepath: String. Path to the directory holding versioned data.
6367
6370
6368 Returns 0 on success.
6371 Returns 0 on success.
6369 """
6372 """
6370 opts = pycompat.byteskwargs(opts)
6373 opts = pycompat.byteskwargs(opts)
6371 with ui.formatter(b'root', opts) as fm:
6374 with ui.formatter(b'root', opts) as fm:
6372 fm.startitem()
6375 fm.startitem()
6373 fm.write(b'reporoot', b'%s\n', repo.root)
6376 fm.write(b'reporoot', b'%s\n', repo.root)
6374 fm.data(hgpath=repo.path, storepath=repo.spath)
6377 fm.data(hgpath=repo.path, storepath=repo.spath)
6375
6378
6376
6379
6377 @command(
6380 @command(
6378 b'serve',
6381 b'serve',
6379 [
6382 [
6380 (
6383 (
6381 b'A',
6384 b'A',
6382 b'accesslog',
6385 b'accesslog',
6383 b'',
6386 b'',
6384 _(b'name of access log file to write to'),
6387 _(b'name of access log file to write to'),
6385 _(b'FILE'),
6388 _(b'FILE'),
6386 ),
6389 ),
6387 (b'd', b'daemon', None, _(b'run server in background')),
6390 (b'd', b'daemon', None, _(b'run server in background')),
6388 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6391 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6389 (
6392 (
6390 b'E',
6393 b'E',
6391 b'errorlog',
6394 b'errorlog',
6392 b'',
6395 b'',
6393 _(b'name of error log file to write to'),
6396 _(b'name of error log file to write to'),
6394 _(b'FILE'),
6397 _(b'FILE'),
6395 ),
6398 ),
6396 # use string type, then we can check if something was passed
6399 # use string type, then we can check if something was passed
6397 (
6400 (
6398 b'p',
6401 b'p',
6399 b'port',
6402 b'port',
6400 b'',
6403 b'',
6401 _(b'port to listen on (default: 8000)'),
6404 _(b'port to listen on (default: 8000)'),
6402 _(b'PORT'),
6405 _(b'PORT'),
6403 ),
6406 ),
6404 (
6407 (
6405 b'a',
6408 b'a',
6406 b'address',
6409 b'address',
6407 b'',
6410 b'',
6408 _(b'address to listen on (default: all interfaces)'),
6411 _(b'address to listen on (default: all interfaces)'),
6409 _(b'ADDR'),
6412 _(b'ADDR'),
6410 ),
6413 ),
6411 (
6414 (
6412 b'',
6415 b'',
6413 b'prefix',
6416 b'prefix',
6414 b'',
6417 b'',
6415 _(b'prefix path to serve from (default: server root)'),
6418 _(b'prefix path to serve from (default: server root)'),
6416 _(b'PREFIX'),
6419 _(b'PREFIX'),
6417 ),
6420 ),
6418 (
6421 (
6419 b'n',
6422 b'n',
6420 b'name',
6423 b'name',
6421 b'',
6424 b'',
6422 _(b'name to show in web pages (default: working directory)'),
6425 _(b'name to show in web pages (default: working directory)'),
6423 _(b'NAME'),
6426 _(b'NAME'),
6424 ),
6427 ),
6425 (
6428 (
6426 b'',
6429 b'',
6427 b'web-conf',
6430 b'web-conf',
6428 b'',
6431 b'',
6429 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6432 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6430 _(b'FILE'),
6433 _(b'FILE'),
6431 ),
6434 ),
6432 (
6435 (
6433 b'',
6436 b'',
6434 b'webdir-conf',
6437 b'webdir-conf',
6435 b'',
6438 b'',
6436 _(b'name of the hgweb config file (DEPRECATED)'),
6439 _(b'name of the hgweb config file (DEPRECATED)'),
6437 _(b'FILE'),
6440 _(b'FILE'),
6438 ),
6441 ),
6439 (
6442 (
6440 b'',
6443 b'',
6441 b'pid-file',
6444 b'pid-file',
6442 b'',
6445 b'',
6443 _(b'name of file to write process ID to'),
6446 _(b'name of file to write process ID to'),
6444 _(b'FILE'),
6447 _(b'FILE'),
6445 ),
6448 ),
6446 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6449 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6447 (
6450 (
6448 b'',
6451 b'',
6449 b'cmdserver',
6452 b'cmdserver',
6450 b'',
6453 b'',
6451 _(b'for remote clients (ADVANCED)'),
6454 _(b'for remote clients (ADVANCED)'),
6452 _(b'MODE'),
6455 _(b'MODE'),
6453 ),
6456 ),
6454 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6457 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6455 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6458 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6456 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6459 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6457 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6460 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6458 (b'', b'print-url', None, _(b'start and print only the URL')),
6461 (b'', b'print-url', None, _(b'start and print only the URL')),
6459 ]
6462 ]
6460 + subrepoopts,
6463 + subrepoopts,
6461 _(b'[OPTION]...'),
6464 _(b'[OPTION]...'),
6462 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6465 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6463 helpbasic=True,
6466 helpbasic=True,
6464 optionalrepo=True,
6467 optionalrepo=True,
6465 )
6468 )
6466 def serve(ui, repo, **opts):
6469 def serve(ui, repo, **opts):
6467 """start stand-alone webserver
6470 """start stand-alone webserver
6468
6471
6469 Start a local HTTP repository browser and pull server. You can use
6472 Start a local HTTP repository browser and pull server. You can use
6470 this for ad-hoc sharing and browsing of repositories. It is
6473 this for ad-hoc sharing and browsing of repositories. It is
6471 recommended to use a real web server to serve a repository for
6474 recommended to use a real web server to serve a repository for
6472 longer periods of time.
6475 longer periods of time.
6473
6476
6474 Please note that the server does not implement access control.
6477 Please note that the server does not implement access control.
6475 This means that, by default, anybody can read from the server and
6478 This means that, by default, anybody can read from the server and
6476 nobody can write to it by default. Set the ``web.allow-push``
6479 nobody can write to it by default. Set the ``web.allow-push``
6477 option to ``*`` to allow everybody to push to the server. You
6480 option to ``*`` to allow everybody to push to the server. You
6478 should use a real web server if you need to authenticate users.
6481 should use a real web server if you need to authenticate users.
6479
6482
6480 By default, the server logs accesses to stdout and errors to
6483 By default, the server logs accesses to stdout and errors to
6481 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6484 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6482 files.
6485 files.
6483
6486
6484 To have the server choose a free port number to listen on, specify
6487 To have the server choose a free port number to listen on, specify
6485 a port number of 0; in this case, the server will print the port
6488 a port number of 0; in this case, the server will print the port
6486 number it uses.
6489 number it uses.
6487
6490
6488 Returns 0 on success.
6491 Returns 0 on success.
6489 """
6492 """
6490
6493
6491 opts = pycompat.byteskwargs(opts)
6494 opts = pycompat.byteskwargs(opts)
6492 if opts[b"stdio"] and opts[b"cmdserver"]:
6495 if opts[b"stdio"] and opts[b"cmdserver"]:
6493 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6496 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6494 if opts[b"print_url"] and ui.verbose:
6497 if opts[b"print_url"] and ui.verbose:
6495 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6498 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6496
6499
6497 if opts[b"stdio"]:
6500 if opts[b"stdio"]:
6498 if repo is None:
6501 if repo is None:
6499 raise error.RepoError(
6502 raise error.RepoError(
6500 _(b"there is no Mercurial repository here (.hg not found)")
6503 _(b"there is no Mercurial repository here (.hg not found)")
6501 )
6504 )
6502 s = wireprotoserver.sshserver(ui, repo)
6505 s = wireprotoserver.sshserver(ui, repo)
6503 s.serve_forever()
6506 s.serve_forever()
6504
6507
6505 service = server.createservice(ui, repo, opts)
6508 service = server.createservice(ui, repo, opts)
6506 return server.runservice(opts, initfn=service.init, runfn=service.run)
6509 return server.runservice(opts, initfn=service.init, runfn=service.run)
6507
6510
6508
6511
6509 @command(
6512 @command(
6510 b'shelve',
6513 b'shelve',
6511 [
6514 [
6512 (
6515 (
6513 b'A',
6516 b'A',
6514 b'addremove',
6517 b'addremove',
6515 None,
6518 None,
6516 _(b'mark new/missing files as added/removed before shelving'),
6519 _(b'mark new/missing files as added/removed before shelving'),
6517 ),
6520 ),
6518 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6521 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6519 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6522 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6520 (
6523 (
6521 b'',
6524 b'',
6522 b'date',
6525 b'date',
6523 b'',
6526 b'',
6524 _(b'shelve with the specified commit date'),
6527 _(b'shelve with the specified commit date'),
6525 _(b'DATE'),
6528 _(b'DATE'),
6526 ),
6529 ),
6527 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6530 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6528 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6531 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6529 (
6532 (
6530 b'k',
6533 b'k',
6531 b'keep',
6534 b'keep',
6532 False,
6535 False,
6533 _(b'shelve, but keep changes in the working directory'),
6536 _(b'shelve, but keep changes in the working directory'),
6534 ),
6537 ),
6535 (b'l', b'list', None, _(b'list current shelves')),
6538 (b'l', b'list', None, _(b'list current shelves')),
6536 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6539 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6537 (
6540 (
6538 b'n',
6541 b'n',
6539 b'name',
6542 b'name',
6540 b'',
6543 b'',
6541 _(b'use the given name for the shelved commit'),
6544 _(b'use the given name for the shelved commit'),
6542 _(b'NAME'),
6545 _(b'NAME'),
6543 ),
6546 ),
6544 (
6547 (
6545 b'p',
6548 b'p',
6546 b'patch',
6549 b'patch',
6547 None,
6550 None,
6548 _(
6551 _(
6549 b'output patches for changes (provide the names of the shelved '
6552 b'output patches for changes (provide the names of the shelved '
6550 b'changes as positional arguments)'
6553 b'changes as positional arguments)'
6551 ),
6554 ),
6552 ),
6555 ),
6553 (b'i', b'interactive', None, _(b'interactive mode')),
6556 (b'i', b'interactive', None, _(b'interactive mode')),
6554 (
6557 (
6555 b'',
6558 b'',
6556 b'stat',
6559 b'stat',
6557 None,
6560 None,
6558 _(
6561 _(
6559 b'output diffstat-style summary of changes (provide the names of '
6562 b'output diffstat-style summary of changes (provide the names of '
6560 b'the shelved changes as positional arguments)'
6563 b'the shelved changes as positional arguments)'
6561 ),
6564 ),
6562 ),
6565 ),
6563 ]
6566 ]
6564 + cmdutil.walkopts,
6567 + cmdutil.walkopts,
6565 _(b'hg shelve [OPTION]... [FILE]...'),
6568 _(b'hg shelve [OPTION]... [FILE]...'),
6566 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6569 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6567 )
6570 )
6568 def shelve(ui, repo, *pats, **opts):
6571 def shelve(ui, repo, *pats, **opts):
6569 '''save and set aside changes from the working directory
6572 '''save and set aside changes from the working directory
6570
6573
6571 Shelving takes files that "hg status" reports as not clean, saves
6574 Shelving takes files that "hg status" reports as not clean, saves
6572 the modifications to a bundle (a shelved change), and reverts the
6575 the modifications to a bundle (a shelved change), and reverts the
6573 files so that their state in the working directory becomes clean.
6576 files so that their state in the working directory becomes clean.
6574
6577
6575 To restore these changes to the working directory, using "hg
6578 To restore these changes to the working directory, using "hg
6576 unshelve"; this will work even if you switch to a different
6579 unshelve"; this will work even if you switch to a different
6577 commit.
6580 commit.
6578
6581
6579 When no files are specified, "hg shelve" saves all not-clean
6582 When no files are specified, "hg shelve" saves all not-clean
6580 files. If specific files or directories are named, only changes to
6583 files. If specific files or directories are named, only changes to
6581 those files are shelved.
6584 those files are shelved.
6582
6585
6583 In bare shelve (when no files are specified, without interactive,
6586 In bare shelve (when no files are specified, without interactive,
6584 include and exclude option), shelving remembers information if the
6587 include and exclude option), shelving remembers information if the
6585 working directory was on newly created branch, in other words working
6588 working directory was on newly created branch, in other words working
6586 directory was on different branch than its first parent. In this
6589 directory was on different branch than its first parent. In this
6587 situation unshelving restores branch information to the working directory.
6590 situation unshelving restores branch information to the working directory.
6588
6591
6589 Each shelved change has a name that makes it easier to find later.
6592 Each shelved change has a name that makes it easier to find later.
6590 The name of a shelved change defaults to being based on the active
6593 The name of a shelved change defaults to being based on the active
6591 bookmark, or if there is no active bookmark, the current named
6594 bookmark, or if there is no active bookmark, the current named
6592 branch. To specify a different name, use ``--name``.
6595 branch. To specify a different name, use ``--name``.
6593
6596
6594 To see a list of existing shelved changes, use the ``--list``
6597 To see a list of existing shelved changes, use the ``--list``
6595 option. For each shelved change, this will print its name, age,
6598 option. For each shelved change, this will print its name, age,
6596 and description; use ``--patch`` or ``--stat`` for more details.
6599 and description; use ``--patch`` or ``--stat`` for more details.
6597
6600
6598 To delete specific shelved changes, use ``--delete``. To delete
6601 To delete specific shelved changes, use ``--delete``. To delete
6599 all shelved changes, use ``--cleanup``.
6602 all shelved changes, use ``--cleanup``.
6600 '''
6603 '''
6601 opts = pycompat.byteskwargs(opts)
6604 opts = pycompat.byteskwargs(opts)
6602 allowables = [
6605 allowables = [
6603 (b'addremove', {b'create'}), # 'create' is pseudo action
6606 (b'addremove', {b'create'}), # 'create' is pseudo action
6604 (b'unknown', {b'create'}),
6607 (b'unknown', {b'create'}),
6605 (b'cleanup', {b'cleanup'}),
6608 (b'cleanup', {b'cleanup'}),
6606 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6609 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6607 (b'delete', {b'delete'}),
6610 (b'delete', {b'delete'}),
6608 (b'edit', {b'create'}),
6611 (b'edit', {b'create'}),
6609 (b'keep', {b'create'}),
6612 (b'keep', {b'create'}),
6610 (b'list', {b'list'}),
6613 (b'list', {b'list'}),
6611 (b'message', {b'create'}),
6614 (b'message', {b'create'}),
6612 (b'name', {b'create'}),
6615 (b'name', {b'create'}),
6613 (b'patch', {b'patch', b'list'}),
6616 (b'patch', {b'patch', b'list'}),
6614 (b'stat', {b'stat', b'list'}),
6617 (b'stat', {b'stat', b'list'}),
6615 ]
6618 ]
6616
6619
6617 def checkopt(opt):
6620 def checkopt(opt):
6618 if opts.get(opt):
6621 if opts.get(opt):
6619 for i, allowable in allowables:
6622 for i, allowable in allowables:
6620 if opts[i] and opt not in allowable:
6623 if opts[i] and opt not in allowable:
6621 raise error.Abort(
6624 raise error.Abort(
6622 _(
6625 _(
6623 b"options '--%s' and '--%s' may not be "
6626 b"options '--%s' and '--%s' may not be "
6624 b"used together"
6627 b"used together"
6625 )
6628 )
6626 % (opt, i)
6629 % (opt, i)
6627 )
6630 )
6628 return True
6631 return True
6629
6632
6630 if checkopt(b'cleanup'):
6633 if checkopt(b'cleanup'):
6631 if pats:
6634 if pats:
6632 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6635 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6633 return shelvemod.cleanupcmd(ui, repo)
6636 return shelvemod.cleanupcmd(ui, repo)
6634 elif checkopt(b'delete'):
6637 elif checkopt(b'delete'):
6635 return shelvemod.deletecmd(ui, repo, pats)
6638 return shelvemod.deletecmd(ui, repo, pats)
6636 elif checkopt(b'list'):
6639 elif checkopt(b'list'):
6637 return shelvemod.listcmd(ui, repo, pats, opts)
6640 return shelvemod.listcmd(ui, repo, pats, opts)
6638 elif checkopt(b'patch') or checkopt(b'stat'):
6641 elif checkopt(b'patch') or checkopt(b'stat'):
6639 return shelvemod.patchcmds(ui, repo, pats, opts)
6642 return shelvemod.patchcmds(ui, repo, pats, opts)
6640 else:
6643 else:
6641 return shelvemod.createcmd(ui, repo, pats, opts)
6644 return shelvemod.createcmd(ui, repo, pats, opts)
6642
6645
6643
6646
6644 _NOTTERSE = b'nothing'
6647 _NOTTERSE = b'nothing'
6645
6648
6646
6649
6647 @command(
6650 @command(
6648 b'status|st',
6651 b'status|st',
6649 [
6652 [
6650 (b'A', b'all', None, _(b'show status of all files')),
6653 (b'A', b'all', None, _(b'show status of all files')),
6651 (b'm', b'modified', None, _(b'show only modified files')),
6654 (b'm', b'modified', None, _(b'show only modified files')),
6652 (b'a', b'added', None, _(b'show only added files')),
6655 (b'a', b'added', None, _(b'show only added files')),
6653 (b'r', b'removed', None, _(b'show only removed files')),
6656 (b'r', b'removed', None, _(b'show only removed files')),
6654 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6657 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6655 (b'c', b'clean', None, _(b'show only files without changes')),
6658 (b'c', b'clean', None, _(b'show only files without changes')),
6656 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6659 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6657 (b'i', b'ignored', None, _(b'show only ignored files')),
6660 (b'i', b'ignored', None, _(b'show only ignored files')),
6658 (b'n', b'no-status', None, _(b'hide status prefix')),
6661 (b'n', b'no-status', None, _(b'hide status prefix')),
6659 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6662 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6660 (b'C', b'copies', None, _(b'show source of copied files')),
6663 (b'C', b'copies', None, _(b'show source of copied files')),
6661 (
6664 (
6662 b'0',
6665 b'0',
6663 b'print0',
6666 b'print0',
6664 None,
6667 None,
6665 _(b'end filenames with NUL, for use with xargs'),
6668 _(b'end filenames with NUL, for use with xargs'),
6666 ),
6669 ),
6667 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6670 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6668 (
6671 (
6669 b'',
6672 b'',
6670 b'change',
6673 b'change',
6671 b'',
6674 b'',
6672 _(b'list the changed files of a revision'),
6675 _(b'list the changed files of a revision'),
6673 _(b'REV'),
6676 _(b'REV'),
6674 ),
6677 ),
6675 ]
6678 ]
6676 + walkopts
6679 + walkopts
6677 + subrepoopts
6680 + subrepoopts
6678 + formatteropts,
6681 + formatteropts,
6679 _(b'[OPTION]... [FILE]...'),
6682 _(b'[OPTION]... [FILE]...'),
6680 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6681 helpbasic=True,
6684 helpbasic=True,
6682 inferrepo=True,
6685 inferrepo=True,
6683 intents={INTENT_READONLY},
6686 intents={INTENT_READONLY},
6684 )
6687 )
6685 def status(ui, repo, *pats, **opts):
6688 def status(ui, repo, *pats, **opts):
6686 """show changed files in the working directory
6689 """show changed files in the working directory
6687
6690
6688 Show status of files in the repository. If names are given, only
6691 Show status of files in the repository. If names are given, only
6689 files that match are shown. Files that are clean or ignored or
6692 files that match are shown. Files that are clean or ignored or
6690 the source of a copy/move operation, are not listed unless
6693 the source of a copy/move operation, are not listed unless
6691 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6694 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6692 Unless options described with "show only ..." are given, the
6695 Unless options described with "show only ..." are given, the
6693 options -mardu are used.
6696 options -mardu are used.
6694
6697
6695 Option -q/--quiet hides untracked (unknown and ignored) files
6698 Option -q/--quiet hides untracked (unknown and ignored) files
6696 unless explicitly requested with -u/--unknown or -i/--ignored.
6699 unless explicitly requested with -u/--unknown or -i/--ignored.
6697
6700
6698 .. note::
6701 .. note::
6699
6702
6700 :hg:`status` may appear to disagree with diff if permissions have
6703 :hg:`status` may appear to disagree with diff if permissions have
6701 changed or a merge has occurred. The standard diff format does
6704 changed or a merge has occurred. The standard diff format does
6702 not report permission changes and diff only reports changes
6705 not report permission changes and diff only reports changes
6703 relative to one merge parent.
6706 relative to one merge parent.
6704
6707
6705 If one revision is given, it is used as the base revision.
6708 If one revision is given, it is used as the base revision.
6706 If two revisions are given, the differences between them are
6709 If two revisions are given, the differences between them are
6707 shown. The --change option can also be used as a shortcut to list
6710 shown. The --change option can also be used as a shortcut to list
6708 the changed files of a revision from its first parent.
6711 the changed files of a revision from its first parent.
6709
6712
6710 The codes used to show the status of files are::
6713 The codes used to show the status of files are::
6711
6714
6712 M = modified
6715 M = modified
6713 A = added
6716 A = added
6714 R = removed
6717 R = removed
6715 C = clean
6718 C = clean
6716 ! = missing (deleted by non-hg command, but still tracked)
6719 ! = missing (deleted by non-hg command, but still tracked)
6717 ? = not tracked
6720 ? = not tracked
6718 I = ignored
6721 I = ignored
6719 = origin of the previous file (with --copies)
6722 = origin of the previous file (with --copies)
6720
6723
6721 .. container:: verbose
6724 .. container:: verbose
6722
6725
6723 The -t/--terse option abbreviates the output by showing only the directory
6726 The -t/--terse option abbreviates the output by showing only the directory
6724 name if all the files in it share the same status. The option takes an
6727 name if all the files in it share the same status. The option takes an
6725 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6728 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6726 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6729 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6727 for 'ignored' and 'c' for clean.
6730 for 'ignored' and 'c' for clean.
6728
6731
6729 It abbreviates only those statuses which are passed. Note that clean and
6732 It abbreviates only those statuses which are passed. Note that clean and
6730 ignored files are not displayed with '--terse ic' unless the -c/--clean
6733 ignored files are not displayed with '--terse ic' unless the -c/--clean
6731 and -i/--ignored options are also used.
6734 and -i/--ignored options are also used.
6732
6735
6733 The -v/--verbose option shows information when the repository is in an
6736 The -v/--verbose option shows information when the repository is in an
6734 unfinished merge, shelve, rebase state etc. You can have this behavior
6737 unfinished merge, shelve, rebase state etc. You can have this behavior
6735 turned on by default by enabling the ``commands.status.verbose`` option.
6738 turned on by default by enabling the ``commands.status.verbose`` option.
6736
6739
6737 You can skip displaying some of these states by setting
6740 You can skip displaying some of these states by setting
6738 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6741 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6739 'histedit', 'merge', 'rebase', or 'unshelve'.
6742 'histedit', 'merge', 'rebase', or 'unshelve'.
6740
6743
6741 Template:
6744 Template:
6742
6745
6743 The following keywords are supported in addition to the common template
6746 The following keywords are supported in addition to the common template
6744 keywords and functions. See also :hg:`help templates`.
6747 keywords and functions. See also :hg:`help templates`.
6745
6748
6746 :path: String. Repository-absolute path of the file.
6749 :path: String. Repository-absolute path of the file.
6747 :source: String. Repository-absolute path of the file originated from.
6750 :source: String. Repository-absolute path of the file originated from.
6748 Available if ``--copies`` is specified.
6751 Available if ``--copies`` is specified.
6749 :status: String. Character denoting file's status.
6752 :status: String. Character denoting file's status.
6750
6753
6751 Examples:
6754 Examples:
6752
6755
6753 - show changes in the working directory relative to a
6756 - show changes in the working directory relative to a
6754 changeset::
6757 changeset::
6755
6758
6756 hg status --rev 9353
6759 hg status --rev 9353
6757
6760
6758 - show changes in the working directory relative to the
6761 - show changes in the working directory relative to the
6759 current directory (see :hg:`help patterns` for more information)::
6762 current directory (see :hg:`help patterns` for more information)::
6760
6763
6761 hg status re:
6764 hg status re:
6762
6765
6763 - show all changes including copies in an existing changeset::
6766 - show all changes including copies in an existing changeset::
6764
6767
6765 hg status --copies --change 9353
6768 hg status --copies --change 9353
6766
6769
6767 - get a NUL separated list of added files, suitable for xargs::
6770 - get a NUL separated list of added files, suitable for xargs::
6768
6771
6769 hg status -an0
6772 hg status -an0
6770
6773
6771 - show more information about the repository status, abbreviating
6774 - show more information about the repository status, abbreviating
6772 added, removed, modified, deleted, and untracked paths::
6775 added, removed, modified, deleted, and untracked paths::
6773
6776
6774 hg status -v -t mardu
6777 hg status -v -t mardu
6775
6778
6776 Returns 0 on success.
6779 Returns 0 on success.
6777
6780
6778 """
6781 """
6779
6782
6780 opts = pycompat.byteskwargs(opts)
6783 opts = pycompat.byteskwargs(opts)
6781 revs = opts.get(b'rev')
6784 revs = opts.get(b'rev')
6782 change = opts.get(b'change')
6785 change = opts.get(b'change')
6783 terse = opts.get(b'terse')
6786 terse = opts.get(b'terse')
6784 if terse is _NOTTERSE:
6787 if terse is _NOTTERSE:
6785 if revs:
6788 if revs:
6786 terse = b''
6789 terse = b''
6787 else:
6790 else:
6788 terse = ui.config(b'commands', b'status.terse')
6791 terse = ui.config(b'commands', b'status.terse')
6789
6792
6790 if revs and change:
6793 if revs and change:
6791 msg = _(b'cannot specify --rev and --change at the same time')
6794 msg = _(b'cannot specify --rev and --change at the same time')
6792 raise error.Abort(msg)
6795 raise error.Abort(msg)
6793 elif revs and terse:
6796 elif revs and terse:
6794 msg = _(b'cannot use --terse with --rev')
6797 msg = _(b'cannot use --terse with --rev')
6795 raise error.Abort(msg)
6798 raise error.Abort(msg)
6796 elif change:
6799 elif change:
6797 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6800 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6798 ctx2 = scmutil.revsingle(repo, change, None)
6801 ctx2 = scmutil.revsingle(repo, change, None)
6799 ctx1 = ctx2.p1()
6802 ctx1 = ctx2.p1()
6800 else:
6803 else:
6801 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6804 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6802 ctx1, ctx2 = scmutil.revpair(repo, revs)
6805 ctx1, ctx2 = scmutil.revpair(repo, revs)
6803
6806
6804 forcerelativevalue = None
6807 forcerelativevalue = None
6805 if ui.hasconfig(b'commands', b'status.relative'):
6808 if ui.hasconfig(b'commands', b'status.relative'):
6806 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6809 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6807 uipathfn = scmutil.getuipathfn(
6810 uipathfn = scmutil.getuipathfn(
6808 repo,
6811 repo,
6809 legacyrelativevalue=bool(pats),
6812 legacyrelativevalue=bool(pats),
6810 forcerelativevalue=forcerelativevalue,
6813 forcerelativevalue=forcerelativevalue,
6811 )
6814 )
6812
6815
6813 if opts.get(b'print0'):
6816 if opts.get(b'print0'):
6814 end = b'\0'
6817 end = b'\0'
6815 else:
6818 else:
6816 end = b'\n'
6819 end = b'\n'
6817 states = b'modified added removed deleted unknown ignored clean'.split()
6820 states = b'modified added removed deleted unknown ignored clean'.split()
6818 show = [k for k in states if opts.get(k)]
6821 show = [k for k in states if opts.get(k)]
6819 if opts.get(b'all'):
6822 if opts.get(b'all'):
6820 show += ui.quiet and (states[:4] + [b'clean']) or states
6823 show += ui.quiet and (states[:4] + [b'clean']) or states
6821
6824
6822 if not show:
6825 if not show:
6823 if ui.quiet:
6826 if ui.quiet:
6824 show = states[:4]
6827 show = states[:4]
6825 else:
6828 else:
6826 show = states[:5]
6829 show = states[:5]
6827
6830
6828 m = scmutil.match(ctx2, pats, opts)
6831 m = scmutil.match(ctx2, pats, opts)
6829 if terse:
6832 if terse:
6830 # we need to compute clean and unknown to terse
6833 # we need to compute clean and unknown to terse
6831 stat = repo.status(
6834 stat = repo.status(
6832 ctx1.node(),
6835 ctx1.node(),
6833 ctx2.node(),
6836 ctx2.node(),
6834 m,
6837 m,
6835 b'ignored' in show or b'i' in terse,
6838 b'ignored' in show or b'i' in terse,
6836 clean=True,
6839 clean=True,
6837 unknown=True,
6840 unknown=True,
6838 listsubrepos=opts.get(b'subrepos'),
6841 listsubrepos=opts.get(b'subrepos'),
6839 )
6842 )
6840
6843
6841 stat = cmdutil.tersedir(stat, terse)
6844 stat = cmdutil.tersedir(stat, terse)
6842 else:
6845 else:
6843 stat = repo.status(
6846 stat = repo.status(
6844 ctx1.node(),
6847 ctx1.node(),
6845 ctx2.node(),
6848 ctx2.node(),
6846 m,
6849 m,
6847 b'ignored' in show,
6850 b'ignored' in show,
6848 b'clean' in show,
6851 b'clean' in show,
6849 b'unknown' in show,
6852 b'unknown' in show,
6850 opts.get(b'subrepos'),
6853 opts.get(b'subrepos'),
6851 )
6854 )
6852
6855
6853 changestates = zip(
6856 changestates = zip(
6854 states,
6857 states,
6855 pycompat.iterbytestr(b'MAR!?IC'),
6858 pycompat.iterbytestr(b'MAR!?IC'),
6856 [getattr(stat, s.decode('utf8')) for s in states],
6859 [getattr(stat, s.decode('utf8')) for s in states],
6857 )
6860 )
6858
6861
6859 copy = {}
6862 copy = {}
6860 if (
6863 if (
6861 opts.get(b'all')
6864 opts.get(b'all')
6862 or opts.get(b'copies')
6865 or opts.get(b'copies')
6863 or ui.configbool(b'ui', b'statuscopies')
6866 or ui.configbool(b'ui', b'statuscopies')
6864 ) and not opts.get(b'no_status'):
6867 ) and not opts.get(b'no_status'):
6865 copy = copies.pathcopies(ctx1, ctx2, m)
6868 copy = copies.pathcopies(ctx1, ctx2, m)
6866
6869
6867 ui.pager(b'status')
6870 ui.pager(b'status')
6868 fm = ui.formatter(b'status', opts)
6871 fm = ui.formatter(b'status', opts)
6869 fmt = b'%s' + end
6872 fmt = b'%s' + end
6870 showchar = not opts.get(b'no_status')
6873 showchar = not opts.get(b'no_status')
6871
6874
6872 for state, char, files in changestates:
6875 for state, char, files in changestates:
6873 if state in show:
6876 if state in show:
6874 label = b'status.' + state
6877 label = b'status.' + state
6875 for f in files:
6878 for f in files:
6876 fm.startitem()
6879 fm.startitem()
6877 fm.context(ctx=ctx2)
6880 fm.context(ctx=ctx2)
6878 fm.data(path=f)
6881 fm.data(path=f)
6879 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6882 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6880 fm.plain(fmt % uipathfn(f), label=label)
6883 fm.plain(fmt % uipathfn(f), label=label)
6881 if f in copy:
6884 if f in copy:
6882 fm.data(source=copy[f])
6885 fm.data(source=copy[f])
6883 fm.plain(
6886 fm.plain(
6884 (b' %s' + end) % uipathfn(copy[f]),
6887 (b' %s' + end) % uipathfn(copy[f]),
6885 label=b'status.copied',
6888 label=b'status.copied',
6886 )
6889 )
6887
6890
6888 if (
6891 if (
6889 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6892 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6890 ) and not ui.plain():
6893 ) and not ui.plain():
6891 cmdutil.morestatus(repo, fm)
6894 cmdutil.morestatus(repo, fm)
6892 fm.end()
6895 fm.end()
6893
6896
6894
6897
6895 @command(
6898 @command(
6896 b'summary|sum',
6899 b'summary|sum',
6897 [(b'', b'remote', None, _(b'check for push and pull'))],
6900 [(b'', b'remote', None, _(b'check for push and pull'))],
6898 b'[--remote]',
6901 b'[--remote]',
6899 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6902 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6900 helpbasic=True,
6903 helpbasic=True,
6901 intents={INTENT_READONLY},
6904 intents={INTENT_READONLY},
6902 )
6905 )
6903 def summary(ui, repo, **opts):
6906 def summary(ui, repo, **opts):
6904 """summarize working directory state
6907 """summarize working directory state
6905
6908
6906 This generates a brief summary of the working directory state,
6909 This generates a brief summary of the working directory state,
6907 including parents, branch, commit status, phase and available updates.
6910 including parents, branch, commit status, phase and available updates.
6908
6911
6909 With the --remote option, this will check the default paths for
6912 With the --remote option, this will check the default paths for
6910 incoming and outgoing changes. This can be time-consuming.
6913 incoming and outgoing changes. This can be time-consuming.
6911
6914
6912 Returns 0 on success.
6915 Returns 0 on success.
6913 """
6916 """
6914
6917
6915 opts = pycompat.byteskwargs(opts)
6918 opts = pycompat.byteskwargs(opts)
6916 ui.pager(b'summary')
6919 ui.pager(b'summary')
6917 ctx = repo[None]
6920 ctx = repo[None]
6918 parents = ctx.parents()
6921 parents = ctx.parents()
6919 pnode = parents[0].node()
6922 pnode = parents[0].node()
6920 marks = []
6923 marks = []
6921
6924
6922 try:
6925 try:
6923 ms = mergemod.mergestate.read(repo)
6926 ms = mergemod.mergestate.read(repo)
6924 except error.UnsupportedMergeRecords as e:
6927 except error.UnsupportedMergeRecords as e:
6925 s = b' '.join(e.recordtypes)
6928 s = b' '.join(e.recordtypes)
6926 ui.warn(
6929 ui.warn(
6927 _(b'warning: merge state has unsupported record types: %s\n') % s
6930 _(b'warning: merge state has unsupported record types: %s\n') % s
6928 )
6931 )
6929 unresolved = []
6932 unresolved = []
6930 else:
6933 else:
6931 unresolved = list(ms.unresolved())
6934 unresolved = list(ms.unresolved())
6932
6935
6933 for p in parents:
6936 for p in parents:
6934 # label with log.changeset (instead of log.parent) since this
6937 # label with log.changeset (instead of log.parent) since this
6935 # shows a working directory parent *changeset*:
6938 # shows a working directory parent *changeset*:
6936 # i18n: column positioning for "hg summary"
6939 # i18n: column positioning for "hg summary"
6937 ui.write(
6940 ui.write(
6938 _(b'parent: %d:%s ') % (p.rev(), p),
6941 _(b'parent: %d:%s ') % (p.rev(), p),
6939 label=logcmdutil.changesetlabels(p),
6942 label=logcmdutil.changesetlabels(p),
6940 )
6943 )
6941 ui.write(b' '.join(p.tags()), label=b'log.tag')
6944 ui.write(b' '.join(p.tags()), label=b'log.tag')
6942 if p.bookmarks():
6945 if p.bookmarks():
6943 marks.extend(p.bookmarks())
6946 marks.extend(p.bookmarks())
6944 if p.rev() == -1:
6947 if p.rev() == -1:
6945 if not len(repo):
6948 if not len(repo):
6946 ui.write(_(b' (empty repository)'))
6949 ui.write(_(b' (empty repository)'))
6947 else:
6950 else:
6948 ui.write(_(b' (no revision checked out)'))
6951 ui.write(_(b' (no revision checked out)'))
6949 if p.obsolete():
6952 if p.obsolete():
6950 ui.write(_(b' (obsolete)'))
6953 ui.write(_(b' (obsolete)'))
6951 if p.isunstable():
6954 if p.isunstable():
6952 instabilities = (
6955 instabilities = (
6953 ui.label(instability, b'trouble.%s' % instability)
6956 ui.label(instability, b'trouble.%s' % instability)
6954 for instability in p.instabilities()
6957 for instability in p.instabilities()
6955 )
6958 )
6956 ui.write(b' (' + b', '.join(instabilities) + b')')
6959 ui.write(b' (' + b', '.join(instabilities) + b')')
6957 ui.write(b'\n')
6960 ui.write(b'\n')
6958 if p.description():
6961 if p.description():
6959 ui.status(
6962 ui.status(
6960 b' ' + p.description().splitlines()[0].strip() + b'\n',
6963 b' ' + p.description().splitlines()[0].strip() + b'\n',
6961 label=b'log.summary',
6964 label=b'log.summary',
6962 )
6965 )
6963
6966
6964 branch = ctx.branch()
6967 branch = ctx.branch()
6965 bheads = repo.branchheads(branch)
6968 bheads = repo.branchheads(branch)
6966 # i18n: column positioning for "hg summary"
6969 # i18n: column positioning for "hg summary"
6967 m = _(b'branch: %s\n') % branch
6970 m = _(b'branch: %s\n') % branch
6968 if branch != b'default':
6971 if branch != b'default':
6969 ui.write(m, label=b'log.branch')
6972 ui.write(m, label=b'log.branch')
6970 else:
6973 else:
6971 ui.status(m, label=b'log.branch')
6974 ui.status(m, label=b'log.branch')
6972
6975
6973 if marks:
6976 if marks:
6974 active = repo._activebookmark
6977 active = repo._activebookmark
6975 # i18n: column positioning for "hg summary"
6978 # i18n: column positioning for "hg summary"
6976 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6979 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6977 if active is not None:
6980 if active is not None:
6978 if active in marks:
6981 if active in marks:
6979 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6982 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6980 marks.remove(active)
6983 marks.remove(active)
6981 else:
6984 else:
6982 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6985 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6983 for m in marks:
6986 for m in marks:
6984 ui.write(b' ' + m, label=b'log.bookmark')
6987 ui.write(b' ' + m, label=b'log.bookmark')
6985 ui.write(b'\n', label=b'log.bookmark')
6988 ui.write(b'\n', label=b'log.bookmark')
6986
6989
6987 status = repo.status(unknown=True)
6990 status = repo.status(unknown=True)
6988
6991
6989 c = repo.dirstate.copies()
6992 c = repo.dirstate.copies()
6990 copied, renamed = [], []
6993 copied, renamed = [], []
6991 for d, s in pycompat.iteritems(c):
6994 for d, s in pycompat.iteritems(c):
6992 if s in status.removed:
6995 if s in status.removed:
6993 status.removed.remove(s)
6996 status.removed.remove(s)
6994 renamed.append(d)
6997 renamed.append(d)
6995 else:
6998 else:
6996 copied.append(d)
6999 copied.append(d)
6997 if d in status.added:
7000 if d in status.added:
6998 status.added.remove(d)
7001 status.added.remove(d)
6999
7002
7000 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7003 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7001
7004
7002 labels = [
7005 labels = [
7003 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7006 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7004 (ui.label(_(b'%d added'), b'status.added'), status.added),
7007 (ui.label(_(b'%d added'), b'status.added'), status.added),
7005 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7008 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7006 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7009 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7007 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7010 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7008 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7011 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7009 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7012 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7010 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7013 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7011 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7014 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7012 ]
7015 ]
7013 t = []
7016 t = []
7014 for l, s in labels:
7017 for l, s in labels:
7015 if s:
7018 if s:
7016 t.append(l % len(s))
7019 t.append(l % len(s))
7017
7020
7018 t = b', '.join(t)
7021 t = b', '.join(t)
7019 cleanworkdir = False
7022 cleanworkdir = False
7020
7023
7021 if repo.vfs.exists(b'graftstate'):
7024 if repo.vfs.exists(b'graftstate'):
7022 t += _(b' (graft in progress)')
7025 t += _(b' (graft in progress)')
7023 if repo.vfs.exists(b'updatestate'):
7026 if repo.vfs.exists(b'updatestate'):
7024 t += _(b' (interrupted update)')
7027 t += _(b' (interrupted update)')
7025 elif len(parents) > 1:
7028 elif len(parents) > 1:
7026 t += _(b' (merge)')
7029 t += _(b' (merge)')
7027 elif branch != parents[0].branch():
7030 elif branch != parents[0].branch():
7028 t += _(b' (new branch)')
7031 t += _(b' (new branch)')
7029 elif parents[0].closesbranch() and pnode in repo.branchheads(
7032 elif parents[0].closesbranch() and pnode in repo.branchheads(
7030 branch, closed=True
7033 branch, closed=True
7031 ):
7034 ):
7032 t += _(b' (head closed)')
7035 t += _(b' (head closed)')
7033 elif not (
7036 elif not (
7034 status.modified
7037 status.modified
7035 or status.added
7038 or status.added
7036 or status.removed
7039 or status.removed
7037 or renamed
7040 or renamed
7038 or copied
7041 or copied
7039 or subs
7042 or subs
7040 ):
7043 ):
7041 t += _(b' (clean)')
7044 t += _(b' (clean)')
7042 cleanworkdir = True
7045 cleanworkdir = True
7043 elif pnode not in bheads:
7046 elif pnode not in bheads:
7044 t += _(b' (new branch head)')
7047 t += _(b' (new branch head)')
7045
7048
7046 if parents:
7049 if parents:
7047 pendingphase = max(p.phase() for p in parents)
7050 pendingphase = max(p.phase() for p in parents)
7048 else:
7051 else:
7049 pendingphase = phases.public
7052 pendingphase = phases.public
7050
7053
7051 if pendingphase > phases.newcommitphase(ui):
7054 if pendingphase > phases.newcommitphase(ui):
7052 t += b' (%s)' % phases.phasenames[pendingphase]
7055 t += b' (%s)' % phases.phasenames[pendingphase]
7053
7056
7054 if cleanworkdir:
7057 if cleanworkdir:
7055 # i18n: column positioning for "hg summary"
7058 # i18n: column positioning for "hg summary"
7056 ui.status(_(b'commit: %s\n') % t.strip())
7059 ui.status(_(b'commit: %s\n') % t.strip())
7057 else:
7060 else:
7058 # i18n: column positioning for "hg summary"
7061 # i18n: column positioning for "hg summary"
7059 ui.write(_(b'commit: %s\n') % t.strip())
7062 ui.write(_(b'commit: %s\n') % t.strip())
7060
7063
7061 # all ancestors of branch heads - all ancestors of parent = new csets
7064 # all ancestors of branch heads - all ancestors of parent = new csets
7062 new = len(
7065 new = len(
7063 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7066 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7064 )
7067 )
7065
7068
7066 if new == 0:
7069 if new == 0:
7067 # i18n: column positioning for "hg summary"
7070 # i18n: column positioning for "hg summary"
7068 ui.status(_(b'update: (current)\n'))
7071 ui.status(_(b'update: (current)\n'))
7069 elif pnode not in bheads:
7072 elif pnode not in bheads:
7070 # i18n: column positioning for "hg summary"
7073 # i18n: column positioning for "hg summary"
7071 ui.write(_(b'update: %d new changesets (update)\n') % new)
7074 ui.write(_(b'update: %d new changesets (update)\n') % new)
7072 else:
7075 else:
7073 # i18n: column positioning for "hg summary"
7076 # i18n: column positioning for "hg summary"
7074 ui.write(
7077 ui.write(
7075 _(b'update: %d new changesets, %d branch heads (merge)\n')
7078 _(b'update: %d new changesets, %d branch heads (merge)\n')
7076 % (new, len(bheads))
7079 % (new, len(bheads))
7077 )
7080 )
7078
7081
7079 t = []
7082 t = []
7080 draft = len(repo.revs(b'draft()'))
7083 draft = len(repo.revs(b'draft()'))
7081 if draft:
7084 if draft:
7082 t.append(_(b'%d draft') % draft)
7085 t.append(_(b'%d draft') % draft)
7083 secret = len(repo.revs(b'secret()'))
7086 secret = len(repo.revs(b'secret()'))
7084 if secret:
7087 if secret:
7085 t.append(_(b'%d secret') % secret)
7088 t.append(_(b'%d secret') % secret)
7086
7089
7087 if draft or secret:
7090 if draft or secret:
7088 ui.status(_(b'phases: %s\n') % b', '.join(t))
7091 ui.status(_(b'phases: %s\n') % b', '.join(t))
7089
7092
7090 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7093 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7091 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7094 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7092 numtrouble = len(repo.revs(trouble + b"()"))
7095 numtrouble = len(repo.revs(trouble + b"()"))
7093 # We write all the possibilities to ease translation
7096 # We write all the possibilities to ease translation
7094 troublemsg = {
7097 troublemsg = {
7095 b"orphan": _(b"orphan: %d changesets"),
7098 b"orphan": _(b"orphan: %d changesets"),
7096 b"contentdivergent": _(b"content-divergent: %d changesets"),
7099 b"contentdivergent": _(b"content-divergent: %d changesets"),
7097 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7100 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7098 }
7101 }
7099 if numtrouble > 0:
7102 if numtrouble > 0:
7100 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7103 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7101
7104
7102 cmdutil.summaryhooks(ui, repo)
7105 cmdutil.summaryhooks(ui, repo)
7103
7106
7104 if opts.get(b'remote'):
7107 if opts.get(b'remote'):
7105 needsincoming, needsoutgoing = True, True
7108 needsincoming, needsoutgoing = True, True
7106 else:
7109 else:
7107 needsincoming, needsoutgoing = False, False
7110 needsincoming, needsoutgoing = False, False
7108 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7111 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7109 if i:
7112 if i:
7110 needsincoming = True
7113 needsincoming = True
7111 if o:
7114 if o:
7112 needsoutgoing = True
7115 needsoutgoing = True
7113 if not needsincoming and not needsoutgoing:
7116 if not needsincoming and not needsoutgoing:
7114 return
7117 return
7115
7118
7116 def getincoming():
7119 def getincoming():
7117 source, branches = hg.parseurl(ui.expandpath(b'default'))
7120 source, branches = hg.parseurl(ui.expandpath(b'default'))
7118 sbranch = branches[0]
7121 sbranch = branches[0]
7119 try:
7122 try:
7120 other = hg.peer(repo, {}, source)
7123 other = hg.peer(repo, {}, source)
7121 except error.RepoError:
7124 except error.RepoError:
7122 if opts.get(b'remote'):
7125 if opts.get(b'remote'):
7123 raise
7126 raise
7124 return source, sbranch, None, None, None
7127 return source, sbranch, None, None, None
7125 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7128 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7126 if revs:
7129 if revs:
7127 revs = [other.lookup(rev) for rev in revs]
7130 revs = [other.lookup(rev) for rev in revs]
7128 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7131 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7129 repo.ui.pushbuffer()
7132 repo.ui.pushbuffer()
7130 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7133 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7131 repo.ui.popbuffer()
7134 repo.ui.popbuffer()
7132 return source, sbranch, other, commoninc, commoninc[1]
7135 return source, sbranch, other, commoninc, commoninc[1]
7133
7136
7134 if needsincoming:
7137 if needsincoming:
7135 source, sbranch, sother, commoninc, incoming = getincoming()
7138 source, sbranch, sother, commoninc, incoming = getincoming()
7136 else:
7139 else:
7137 source = sbranch = sother = commoninc = incoming = None
7140 source = sbranch = sother = commoninc = incoming = None
7138
7141
7139 def getoutgoing():
7142 def getoutgoing():
7140 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7143 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7141 dbranch = branches[0]
7144 dbranch = branches[0]
7142 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7145 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7143 if source != dest:
7146 if source != dest:
7144 try:
7147 try:
7145 dother = hg.peer(repo, {}, dest)
7148 dother = hg.peer(repo, {}, dest)
7146 except error.RepoError:
7149 except error.RepoError:
7147 if opts.get(b'remote'):
7150 if opts.get(b'remote'):
7148 raise
7151 raise
7149 return dest, dbranch, None, None
7152 return dest, dbranch, None, None
7150 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7153 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7151 elif sother is None:
7154 elif sother is None:
7152 # there is no explicit destination peer, but source one is invalid
7155 # there is no explicit destination peer, but source one is invalid
7153 return dest, dbranch, None, None
7156 return dest, dbranch, None, None
7154 else:
7157 else:
7155 dother = sother
7158 dother = sother
7156 if source != dest or (sbranch is not None and sbranch != dbranch):
7159 if source != dest or (sbranch is not None and sbranch != dbranch):
7157 common = None
7160 common = None
7158 else:
7161 else:
7159 common = commoninc
7162 common = commoninc
7160 if revs:
7163 if revs:
7161 revs = [repo.lookup(rev) for rev in revs]
7164 revs = [repo.lookup(rev) for rev in revs]
7162 repo.ui.pushbuffer()
7165 repo.ui.pushbuffer()
7163 outgoing = discovery.findcommonoutgoing(
7166 outgoing = discovery.findcommonoutgoing(
7164 repo, dother, onlyheads=revs, commoninc=common
7167 repo, dother, onlyheads=revs, commoninc=common
7165 )
7168 )
7166 repo.ui.popbuffer()
7169 repo.ui.popbuffer()
7167 return dest, dbranch, dother, outgoing
7170 return dest, dbranch, dother, outgoing
7168
7171
7169 if needsoutgoing:
7172 if needsoutgoing:
7170 dest, dbranch, dother, outgoing = getoutgoing()
7173 dest, dbranch, dother, outgoing = getoutgoing()
7171 else:
7174 else:
7172 dest = dbranch = dother = outgoing = None
7175 dest = dbranch = dother = outgoing = None
7173
7176
7174 if opts.get(b'remote'):
7177 if opts.get(b'remote'):
7175 t = []
7178 t = []
7176 if incoming:
7179 if incoming:
7177 t.append(_(b'1 or more incoming'))
7180 t.append(_(b'1 or more incoming'))
7178 o = outgoing.missing
7181 o = outgoing.missing
7179 if o:
7182 if o:
7180 t.append(_(b'%d outgoing') % len(o))
7183 t.append(_(b'%d outgoing') % len(o))
7181 other = dother or sother
7184 other = dother or sother
7182 if b'bookmarks' in other.listkeys(b'namespaces'):
7185 if b'bookmarks' in other.listkeys(b'namespaces'):
7183 counts = bookmarks.summary(repo, other)
7186 counts = bookmarks.summary(repo, other)
7184 if counts[0] > 0:
7187 if counts[0] > 0:
7185 t.append(_(b'%d incoming bookmarks') % counts[0])
7188 t.append(_(b'%d incoming bookmarks') % counts[0])
7186 if counts[1] > 0:
7189 if counts[1] > 0:
7187 t.append(_(b'%d outgoing bookmarks') % counts[1])
7190 t.append(_(b'%d outgoing bookmarks') % counts[1])
7188
7191
7189 if t:
7192 if t:
7190 # i18n: column positioning for "hg summary"
7193 # i18n: column positioning for "hg summary"
7191 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7194 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7192 else:
7195 else:
7193 # i18n: column positioning for "hg summary"
7196 # i18n: column positioning for "hg summary"
7194 ui.status(_(b'remote: (synced)\n'))
7197 ui.status(_(b'remote: (synced)\n'))
7195
7198
7196 cmdutil.summaryremotehooks(
7199 cmdutil.summaryremotehooks(
7197 ui,
7200 ui,
7198 repo,
7201 repo,
7199 opts,
7202 opts,
7200 (
7203 (
7201 (source, sbranch, sother, commoninc),
7204 (source, sbranch, sother, commoninc),
7202 (dest, dbranch, dother, outgoing),
7205 (dest, dbranch, dother, outgoing),
7203 ),
7206 ),
7204 )
7207 )
7205
7208
7206
7209
7207 @command(
7210 @command(
7208 b'tag',
7211 b'tag',
7209 [
7212 [
7210 (b'f', b'force', None, _(b'force tag')),
7213 (b'f', b'force', None, _(b'force tag')),
7211 (b'l', b'local', None, _(b'make the tag local')),
7214 (b'l', b'local', None, _(b'make the tag local')),
7212 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7215 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7213 (b'', b'remove', None, _(b'remove a tag')),
7216 (b'', b'remove', None, _(b'remove a tag')),
7214 # -l/--local is already there, commitopts cannot be used
7217 # -l/--local is already there, commitopts cannot be used
7215 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7218 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7216 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7219 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7217 ]
7220 ]
7218 + commitopts2,
7221 + commitopts2,
7219 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7222 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7220 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7223 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7221 )
7224 )
7222 def tag(ui, repo, name1, *names, **opts):
7225 def tag(ui, repo, name1, *names, **opts):
7223 """add one or more tags for the current or given revision
7226 """add one or more tags for the current or given revision
7224
7227
7225 Name a particular revision using <name>.
7228 Name a particular revision using <name>.
7226
7229
7227 Tags are used to name particular revisions of the repository and are
7230 Tags are used to name particular revisions of the repository and are
7228 very useful to compare different revisions, to go back to significant
7231 very useful to compare different revisions, to go back to significant
7229 earlier versions or to mark branch points as releases, etc. Changing
7232 earlier versions or to mark branch points as releases, etc. Changing
7230 an existing tag is normally disallowed; use -f/--force to override.
7233 an existing tag is normally disallowed; use -f/--force to override.
7231
7234
7232 If no revision is given, the parent of the working directory is
7235 If no revision is given, the parent of the working directory is
7233 used.
7236 used.
7234
7237
7235 To facilitate version control, distribution, and merging of tags,
7238 To facilitate version control, distribution, and merging of tags,
7236 they are stored as a file named ".hgtags" which is managed similarly
7239 they are stored as a file named ".hgtags" which is managed similarly
7237 to other project files and can be hand-edited if necessary. This
7240 to other project files and can be hand-edited if necessary. This
7238 also means that tagging creates a new commit. The file
7241 also means that tagging creates a new commit. The file
7239 ".hg/localtags" is used for local tags (not shared among
7242 ".hg/localtags" is used for local tags (not shared among
7240 repositories).
7243 repositories).
7241
7244
7242 Tag commits are usually made at the head of a branch. If the parent
7245 Tag commits are usually made at the head of a branch. If the parent
7243 of the working directory is not a branch head, :hg:`tag` aborts; use
7246 of the working directory is not a branch head, :hg:`tag` aborts; use
7244 -f/--force to force the tag commit to be based on a non-head
7247 -f/--force to force the tag commit to be based on a non-head
7245 changeset.
7248 changeset.
7246
7249
7247 See :hg:`help dates` for a list of formats valid for -d/--date.
7250 See :hg:`help dates` for a list of formats valid for -d/--date.
7248
7251
7249 Since tag names have priority over branch names during revision
7252 Since tag names have priority over branch names during revision
7250 lookup, using an existing branch name as a tag name is discouraged.
7253 lookup, using an existing branch name as a tag name is discouraged.
7251
7254
7252 Returns 0 on success.
7255 Returns 0 on success.
7253 """
7256 """
7254 opts = pycompat.byteskwargs(opts)
7257 opts = pycompat.byteskwargs(opts)
7255 with repo.wlock(), repo.lock():
7258 with repo.wlock(), repo.lock():
7256 rev_ = b"."
7259 rev_ = b"."
7257 names = [t.strip() for t in (name1,) + names]
7260 names = [t.strip() for t in (name1,) + names]
7258 if len(names) != len(set(names)):
7261 if len(names) != len(set(names)):
7259 raise error.Abort(_(b'tag names must be unique'))
7262 raise error.Abort(_(b'tag names must be unique'))
7260 for n in names:
7263 for n in names:
7261 scmutil.checknewlabel(repo, n, b'tag')
7264 scmutil.checknewlabel(repo, n, b'tag')
7262 if not n:
7265 if not n:
7263 raise error.Abort(
7266 raise error.Abort(
7264 _(b'tag names cannot consist entirely of whitespace')
7267 _(b'tag names cannot consist entirely of whitespace')
7265 )
7268 )
7266 if opts.get(b'rev') and opts.get(b'remove'):
7269 if opts.get(b'rev') and opts.get(b'remove'):
7267 raise error.Abort(_(b"--rev and --remove are incompatible"))
7270 raise error.Abort(_(b"--rev and --remove are incompatible"))
7268 if opts.get(b'rev'):
7271 if opts.get(b'rev'):
7269 rev_ = opts[b'rev']
7272 rev_ = opts[b'rev']
7270 message = opts.get(b'message')
7273 message = opts.get(b'message')
7271 if opts.get(b'remove'):
7274 if opts.get(b'remove'):
7272 if opts.get(b'local'):
7275 if opts.get(b'local'):
7273 expectedtype = b'local'
7276 expectedtype = b'local'
7274 else:
7277 else:
7275 expectedtype = b'global'
7278 expectedtype = b'global'
7276
7279
7277 for n in names:
7280 for n in names:
7278 if repo.tagtype(n) == b'global':
7281 if repo.tagtype(n) == b'global':
7279 alltags = tagsmod.findglobaltags(ui, repo)
7282 alltags = tagsmod.findglobaltags(ui, repo)
7280 if alltags[n][0] == nullid:
7283 if alltags[n][0] == nullid:
7281 raise error.Abort(_(b"tag '%s' is already removed") % n)
7284 raise error.Abort(_(b"tag '%s' is already removed") % n)
7282 if not repo.tagtype(n):
7285 if not repo.tagtype(n):
7283 raise error.Abort(_(b"tag '%s' does not exist") % n)
7286 raise error.Abort(_(b"tag '%s' does not exist") % n)
7284 if repo.tagtype(n) != expectedtype:
7287 if repo.tagtype(n) != expectedtype:
7285 if expectedtype == b'global':
7288 if expectedtype == b'global':
7286 raise error.Abort(
7289 raise error.Abort(
7287 _(b"tag '%s' is not a global tag") % n
7290 _(b"tag '%s' is not a global tag") % n
7288 )
7291 )
7289 else:
7292 else:
7290 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7293 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7291 rev_ = b'null'
7294 rev_ = b'null'
7292 if not message:
7295 if not message:
7293 # we don't translate commit messages
7296 # we don't translate commit messages
7294 message = b'Removed tag %s' % b', '.join(names)
7297 message = b'Removed tag %s' % b', '.join(names)
7295 elif not opts.get(b'force'):
7298 elif not opts.get(b'force'):
7296 for n in names:
7299 for n in names:
7297 if n in repo.tags():
7300 if n in repo.tags():
7298 raise error.Abort(
7301 raise error.Abort(
7299 _(b"tag '%s' already exists (use -f to force)") % n
7302 _(b"tag '%s' already exists (use -f to force)") % n
7300 )
7303 )
7301 if not opts.get(b'local'):
7304 if not opts.get(b'local'):
7302 p1, p2 = repo.dirstate.parents()
7305 p1, p2 = repo.dirstate.parents()
7303 if p2 != nullid:
7306 if p2 != nullid:
7304 raise error.Abort(_(b'uncommitted merge'))
7307 raise error.Abort(_(b'uncommitted merge'))
7305 bheads = repo.branchheads()
7308 bheads = repo.branchheads()
7306 if not opts.get(b'force') and bheads and p1 not in bheads:
7309 if not opts.get(b'force') and bheads and p1 not in bheads:
7307 raise error.Abort(
7310 raise error.Abort(
7308 _(
7311 _(
7309 b'working directory is not at a branch head '
7312 b'working directory is not at a branch head '
7310 b'(use -f to force)'
7313 b'(use -f to force)'
7311 )
7314 )
7312 )
7315 )
7313 node = scmutil.revsingle(repo, rev_).node()
7316 node = scmutil.revsingle(repo, rev_).node()
7314
7317
7315 if not message:
7318 if not message:
7316 # we don't translate commit messages
7319 # we don't translate commit messages
7317 message = b'Added tag %s for changeset %s' % (
7320 message = b'Added tag %s for changeset %s' % (
7318 b', '.join(names),
7321 b', '.join(names),
7319 short(node),
7322 short(node),
7320 )
7323 )
7321
7324
7322 date = opts.get(b'date')
7325 date = opts.get(b'date')
7323 if date:
7326 if date:
7324 date = dateutil.parsedate(date)
7327 date = dateutil.parsedate(date)
7325
7328
7326 if opts.get(b'remove'):
7329 if opts.get(b'remove'):
7327 editform = b'tag.remove'
7330 editform = b'tag.remove'
7328 else:
7331 else:
7329 editform = b'tag.add'
7332 editform = b'tag.add'
7330 editor = cmdutil.getcommiteditor(
7333 editor = cmdutil.getcommiteditor(
7331 editform=editform, **pycompat.strkwargs(opts)
7334 editform=editform, **pycompat.strkwargs(opts)
7332 )
7335 )
7333
7336
7334 # don't allow tagging the null rev
7337 # don't allow tagging the null rev
7335 if (
7338 if (
7336 not opts.get(b'remove')
7339 not opts.get(b'remove')
7337 and scmutil.revsingle(repo, rev_).rev() == nullrev
7340 and scmutil.revsingle(repo, rev_).rev() == nullrev
7338 ):
7341 ):
7339 raise error.Abort(_(b"cannot tag null revision"))
7342 raise error.Abort(_(b"cannot tag null revision"))
7340
7343
7341 tagsmod.tag(
7344 tagsmod.tag(
7342 repo,
7345 repo,
7343 names,
7346 names,
7344 node,
7347 node,
7345 message,
7348 message,
7346 opts.get(b'local'),
7349 opts.get(b'local'),
7347 opts.get(b'user'),
7350 opts.get(b'user'),
7348 date,
7351 date,
7349 editor=editor,
7352 editor=editor,
7350 )
7353 )
7351
7354
7352
7355
7353 @command(
7356 @command(
7354 b'tags',
7357 b'tags',
7355 formatteropts,
7358 formatteropts,
7356 b'',
7359 b'',
7357 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7360 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7358 intents={INTENT_READONLY},
7361 intents={INTENT_READONLY},
7359 )
7362 )
7360 def tags(ui, repo, **opts):
7363 def tags(ui, repo, **opts):
7361 """list repository tags
7364 """list repository tags
7362
7365
7363 This lists both regular and local tags. When the -v/--verbose
7366 This lists both regular and local tags. When the -v/--verbose
7364 switch is used, a third column "local" is printed for local tags.
7367 switch is used, a third column "local" is printed for local tags.
7365 When the -q/--quiet switch is used, only the tag name is printed.
7368 When the -q/--quiet switch is used, only the tag name is printed.
7366
7369
7367 .. container:: verbose
7370 .. container:: verbose
7368
7371
7369 Template:
7372 Template:
7370
7373
7371 The following keywords are supported in addition to the common template
7374 The following keywords are supported in addition to the common template
7372 keywords and functions such as ``{tag}``. See also
7375 keywords and functions such as ``{tag}``. See also
7373 :hg:`help templates`.
7376 :hg:`help templates`.
7374
7377
7375 :type: String. ``local`` for local tags.
7378 :type: String. ``local`` for local tags.
7376
7379
7377 Returns 0 on success.
7380 Returns 0 on success.
7378 """
7381 """
7379
7382
7380 opts = pycompat.byteskwargs(opts)
7383 opts = pycompat.byteskwargs(opts)
7381 ui.pager(b'tags')
7384 ui.pager(b'tags')
7382 fm = ui.formatter(b'tags', opts)
7385 fm = ui.formatter(b'tags', opts)
7383 hexfunc = fm.hexfunc
7386 hexfunc = fm.hexfunc
7384
7387
7385 for t, n in reversed(repo.tagslist()):
7388 for t, n in reversed(repo.tagslist()):
7386 hn = hexfunc(n)
7389 hn = hexfunc(n)
7387 label = b'tags.normal'
7390 label = b'tags.normal'
7388 tagtype = b''
7391 tagtype = b''
7389 if repo.tagtype(t) == b'local':
7392 if repo.tagtype(t) == b'local':
7390 label = b'tags.local'
7393 label = b'tags.local'
7391 tagtype = b'local'
7394 tagtype = b'local'
7392
7395
7393 fm.startitem()
7396 fm.startitem()
7394 fm.context(repo=repo)
7397 fm.context(repo=repo)
7395 fm.write(b'tag', b'%s', t, label=label)
7398 fm.write(b'tag', b'%s', t, label=label)
7396 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7399 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7397 fm.condwrite(
7400 fm.condwrite(
7398 not ui.quiet,
7401 not ui.quiet,
7399 b'rev node',
7402 b'rev node',
7400 fmt,
7403 fmt,
7401 repo.changelog.rev(n),
7404 repo.changelog.rev(n),
7402 hn,
7405 hn,
7403 label=label,
7406 label=label,
7404 )
7407 )
7405 fm.condwrite(
7408 fm.condwrite(
7406 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7409 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7407 )
7410 )
7408 fm.plain(b'\n')
7411 fm.plain(b'\n')
7409 fm.end()
7412 fm.end()
7410
7413
7411
7414
7412 @command(
7415 @command(
7413 b'tip',
7416 b'tip',
7414 [
7417 [
7415 (b'p', b'patch', None, _(b'show patch')),
7418 (b'p', b'patch', None, _(b'show patch')),
7416 (b'g', b'git', None, _(b'use git extended diff format')),
7419 (b'g', b'git', None, _(b'use git extended diff format')),
7417 ]
7420 ]
7418 + templateopts,
7421 + templateopts,
7419 _(b'[-p] [-g]'),
7422 _(b'[-p] [-g]'),
7420 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7423 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7421 )
7424 )
7422 def tip(ui, repo, **opts):
7425 def tip(ui, repo, **opts):
7423 """show the tip revision (DEPRECATED)
7426 """show the tip revision (DEPRECATED)
7424
7427
7425 The tip revision (usually just called the tip) is the changeset
7428 The tip revision (usually just called the tip) is the changeset
7426 most recently added to the repository (and therefore the most
7429 most recently added to the repository (and therefore the most
7427 recently changed head).
7430 recently changed head).
7428
7431
7429 If you have just made a commit, that commit will be the tip. If
7432 If you have just made a commit, that commit will be the tip. If
7430 you have just pulled changes from another repository, the tip of
7433 you have just pulled changes from another repository, the tip of
7431 that repository becomes the current tip. The "tip" tag is special
7434 that repository becomes the current tip. The "tip" tag is special
7432 and cannot be renamed or assigned to a different changeset.
7435 and cannot be renamed or assigned to a different changeset.
7433
7436
7434 This command is deprecated, please use :hg:`heads` instead.
7437 This command is deprecated, please use :hg:`heads` instead.
7435
7438
7436 Returns 0 on success.
7439 Returns 0 on success.
7437 """
7440 """
7438 opts = pycompat.byteskwargs(opts)
7441 opts = pycompat.byteskwargs(opts)
7439 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7442 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7440 displayer.show(repo[b'tip'])
7443 displayer.show(repo[b'tip'])
7441 displayer.close()
7444 displayer.close()
7442
7445
7443
7446
7444 @command(
7447 @command(
7445 b'unbundle',
7448 b'unbundle',
7446 [
7449 [
7447 (
7450 (
7448 b'u',
7451 b'u',
7449 b'update',
7452 b'update',
7450 None,
7453 None,
7451 _(b'update to new branch head if changesets were unbundled'),
7454 _(b'update to new branch head if changesets were unbundled'),
7452 )
7455 )
7453 ],
7456 ],
7454 _(b'[-u] FILE...'),
7457 _(b'[-u] FILE...'),
7455 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7458 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7456 )
7459 )
7457 def unbundle(ui, repo, fname1, *fnames, **opts):
7460 def unbundle(ui, repo, fname1, *fnames, **opts):
7458 """apply one or more bundle files
7461 """apply one or more bundle files
7459
7462
7460 Apply one or more bundle files generated by :hg:`bundle`.
7463 Apply one or more bundle files generated by :hg:`bundle`.
7461
7464
7462 Returns 0 on success, 1 if an update has unresolved files.
7465 Returns 0 on success, 1 if an update has unresolved files.
7463 """
7466 """
7464 fnames = (fname1,) + fnames
7467 fnames = (fname1,) + fnames
7465
7468
7466 with repo.lock():
7469 with repo.lock():
7467 for fname in fnames:
7470 for fname in fnames:
7468 f = hg.openpath(ui, fname)
7471 f = hg.openpath(ui, fname)
7469 gen = exchange.readbundle(ui, f, fname)
7472 gen = exchange.readbundle(ui, f, fname)
7470 if isinstance(gen, streamclone.streamcloneapplier):
7473 if isinstance(gen, streamclone.streamcloneapplier):
7471 raise error.Abort(
7474 raise error.Abort(
7472 _(
7475 _(
7473 b'packed bundles cannot be applied with '
7476 b'packed bundles cannot be applied with '
7474 b'"hg unbundle"'
7477 b'"hg unbundle"'
7475 ),
7478 ),
7476 hint=_(b'use "hg debugapplystreamclonebundle"'),
7479 hint=_(b'use "hg debugapplystreamclonebundle"'),
7477 )
7480 )
7478 url = b'bundle:' + fname
7481 url = b'bundle:' + fname
7479 try:
7482 try:
7480 txnname = b'unbundle'
7483 txnname = b'unbundle'
7481 if not isinstance(gen, bundle2.unbundle20):
7484 if not isinstance(gen, bundle2.unbundle20):
7482 txnname = b'unbundle\n%s' % util.hidepassword(url)
7485 txnname = b'unbundle\n%s' % util.hidepassword(url)
7483 with repo.transaction(txnname) as tr:
7486 with repo.transaction(txnname) as tr:
7484 op = bundle2.applybundle(
7487 op = bundle2.applybundle(
7485 repo, gen, tr, source=b'unbundle', url=url
7488 repo, gen, tr, source=b'unbundle', url=url
7486 )
7489 )
7487 except error.BundleUnknownFeatureError as exc:
7490 except error.BundleUnknownFeatureError as exc:
7488 raise error.Abort(
7491 raise error.Abort(
7489 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7492 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7490 hint=_(
7493 hint=_(
7491 b"see https://mercurial-scm.org/"
7494 b"see https://mercurial-scm.org/"
7492 b"wiki/BundleFeature for more "
7495 b"wiki/BundleFeature for more "
7493 b"information"
7496 b"information"
7494 ),
7497 ),
7495 )
7498 )
7496 modheads = bundle2.combinechangegroupresults(op)
7499 modheads = bundle2.combinechangegroupresults(op)
7497
7500
7498 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7501 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7499
7502
7500
7503
7501 @command(
7504 @command(
7502 b'unshelve',
7505 b'unshelve',
7503 [
7506 [
7504 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7507 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7505 (
7508 (
7506 b'c',
7509 b'c',
7507 b'continue',
7510 b'continue',
7508 None,
7511 None,
7509 _(b'continue an incomplete unshelve operation'),
7512 _(b'continue an incomplete unshelve operation'),
7510 ),
7513 ),
7511 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7514 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7512 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7515 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7513 (
7516 (
7514 b'n',
7517 b'n',
7515 b'name',
7518 b'name',
7516 b'',
7519 b'',
7517 _(b'restore shelved change with given name'),
7520 _(b'restore shelved change with given name'),
7518 _(b'NAME'),
7521 _(b'NAME'),
7519 ),
7522 ),
7520 (b't', b'tool', b'', _(b'specify merge tool')),
7523 (b't', b'tool', b'', _(b'specify merge tool')),
7521 (
7524 (
7522 b'',
7525 b'',
7523 b'date',
7526 b'date',
7524 b'',
7527 b'',
7525 _(b'set date for temporary commits (DEPRECATED)'),
7528 _(b'set date for temporary commits (DEPRECATED)'),
7526 _(b'DATE'),
7529 _(b'DATE'),
7527 ),
7530 ),
7528 ],
7531 ],
7529 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7532 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7530 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7533 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7531 )
7534 )
7532 def unshelve(ui, repo, *shelved, **opts):
7535 def unshelve(ui, repo, *shelved, **opts):
7533 """restore a shelved change to the working directory
7536 """restore a shelved change to the working directory
7534
7537
7535 This command accepts an optional name of a shelved change to
7538 This command accepts an optional name of a shelved change to
7536 restore. If none is given, the most recent shelved change is used.
7539 restore. If none is given, the most recent shelved change is used.
7537
7540
7538 If a shelved change is applied successfully, the bundle that
7541 If a shelved change is applied successfully, the bundle that
7539 contains the shelved changes is moved to a backup location
7542 contains the shelved changes is moved to a backup location
7540 (.hg/shelve-backup).
7543 (.hg/shelve-backup).
7541
7544
7542 Since you can restore a shelved change on top of an arbitrary
7545 Since you can restore a shelved change on top of an arbitrary
7543 commit, it is possible that unshelving will result in a conflict
7546 commit, it is possible that unshelving will result in a conflict
7544 between your changes and the commits you are unshelving onto. If
7547 between your changes and the commits you are unshelving onto. If
7545 this occurs, you must resolve the conflict, then use
7548 this occurs, you must resolve the conflict, then use
7546 ``--continue`` to complete the unshelve operation. (The bundle
7549 ``--continue`` to complete the unshelve operation. (The bundle
7547 will not be moved until you successfully complete the unshelve.)
7550 will not be moved until you successfully complete the unshelve.)
7548
7551
7549 (Alternatively, you can use ``--abort`` to abandon an unshelve
7552 (Alternatively, you can use ``--abort`` to abandon an unshelve
7550 that causes a conflict. This reverts the unshelved changes, and
7553 that causes a conflict. This reverts the unshelved changes, and
7551 leaves the bundle in place.)
7554 leaves the bundle in place.)
7552
7555
7553 If bare shelved change (without interactive, include and exclude
7556 If bare shelved change (without interactive, include and exclude
7554 option) was done on newly created branch it would restore branch
7557 option) was done on newly created branch it would restore branch
7555 information to the working directory.
7558 information to the working directory.
7556
7559
7557 After a successful unshelve, the shelved changes are stored in a
7560 After a successful unshelve, the shelved changes are stored in a
7558 backup directory. Only the N most recent backups are kept. N
7561 backup directory. Only the N most recent backups are kept. N
7559 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7562 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7560 configuration option.
7563 configuration option.
7561
7564
7562 .. container:: verbose
7565 .. container:: verbose
7563
7566
7564 Timestamp in seconds is used to decide order of backups. More
7567 Timestamp in seconds is used to decide order of backups. More
7565 than ``maxbackups`` backups are kept, if same timestamp
7568 than ``maxbackups`` backups are kept, if same timestamp
7566 prevents from deciding exact order of them, for safety.
7569 prevents from deciding exact order of them, for safety.
7567
7570
7568 Selected changes can be unshelved with ``--interactive`` flag.
7571 Selected changes can be unshelved with ``--interactive`` flag.
7569 The working directory is updated with the selected changes, and
7572 The working directory is updated with the selected changes, and
7570 only the unselected changes remain shelved.
7573 only the unselected changes remain shelved.
7571 Note: The whole shelve is applied to working directory first before
7574 Note: The whole shelve is applied to working directory first before
7572 running interactively. So, this will bring up all the conflicts between
7575 running interactively. So, this will bring up all the conflicts between
7573 working directory and the shelve, irrespective of which changes will be
7576 working directory and the shelve, irrespective of which changes will be
7574 unshelved.
7577 unshelved.
7575 """
7578 """
7576 with repo.wlock():
7579 with repo.wlock():
7577 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7580 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7578
7581
7579
7582
7580 statemod.addunfinished(
7583 statemod.addunfinished(
7581 b'unshelve',
7584 b'unshelve',
7582 fname=b'shelvedstate',
7585 fname=b'shelvedstate',
7583 continueflag=True,
7586 continueflag=True,
7584 abortfunc=shelvemod.hgabortunshelve,
7587 abortfunc=shelvemod.hgabortunshelve,
7585 continuefunc=shelvemod.hgcontinueunshelve,
7588 continuefunc=shelvemod.hgcontinueunshelve,
7586 cmdmsg=_(b'unshelve already in progress'),
7589 cmdmsg=_(b'unshelve already in progress'),
7587 )
7590 )
7588
7591
7589
7592
7590 @command(
7593 @command(
7591 b'update|up|checkout|co',
7594 b'update|up|checkout|co',
7592 [
7595 [
7593 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7596 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7594 (b'c', b'check', None, _(b'require clean working directory')),
7597 (b'c', b'check', None, _(b'require clean working directory')),
7595 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7598 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7596 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7599 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7597 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7600 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7598 ]
7601 ]
7599 + mergetoolopts,
7602 + mergetoolopts,
7600 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7603 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7601 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7604 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7602 helpbasic=True,
7605 helpbasic=True,
7603 )
7606 )
7604 def update(ui, repo, node=None, **opts):
7607 def update(ui, repo, node=None, **opts):
7605 """update working directory (or switch revisions)
7608 """update working directory (or switch revisions)
7606
7609
7607 Update the repository's working directory to the specified
7610 Update the repository's working directory to the specified
7608 changeset. If no changeset is specified, update to the tip of the
7611 changeset. If no changeset is specified, update to the tip of the
7609 current named branch and move the active bookmark (see :hg:`help
7612 current named branch and move the active bookmark (see :hg:`help
7610 bookmarks`).
7613 bookmarks`).
7611
7614
7612 Update sets the working directory's parent revision to the specified
7615 Update sets the working directory's parent revision to the specified
7613 changeset (see :hg:`help parents`).
7616 changeset (see :hg:`help parents`).
7614
7617
7615 If the changeset is not a descendant or ancestor of the working
7618 If the changeset is not a descendant or ancestor of the working
7616 directory's parent and there are uncommitted changes, the update is
7619 directory's parent and there are uncommitted changes, the update is
7617 aborted. With the -c/--check option, the working directory is checked
7620 aborted. With the -c/--check option, the working directory is checked
7618 for uncommitted changes; if none are found, the working directory is
7621 for uncommitted changes; if none are found, the working directory is
7619 updated to the specified changeset.
7622 updated to the specified changeset.
7620
7623
7621 .. container:: verbose
7624 .. container:: verbose
7622
7625
7623 The -C/--clean, -c/--check, and -m/--merge options control what
7626 The -C/--clean, -c/--check, and -m/--merge options control what
7624 happens if the working directory contains uncommitted changes.
7627 happens if the working directory contains uncommitted changes.
7625 At most of one of them can be specified.
7628 At most of one of them can be specified.
7626
7629
7627 1. If no option is specified, and if
7630 1. If no option is specified, and if
7628 the requested changeset is an ancestor or descendant of
7631 the requested changeset is an ancestor or descendant of
7629 the working directory's parent, the uncommitted changes
7632 the working directory's parent, the uncommitted changes
7630 are merged into the requested changeset and the merged
7633 are merged into the requested changeset and the merged
7631 result is left uncommitted. If the requested changeset is
7634 result is left uncommitted. If the requested changeset is
7632 not an ancestor or descendant (that is, it is on another
7635 not an ancestor or descendant (that is, it is on another
7633 branch), the update is aborted and the uncommitted changes
7636 branch), the update is aborted and the uncommitted changes
7634 are preserved.
7637 are preserved.
7635
7638
7636 2. With the -m/--merge option, the update is allowed even if the
7639 2. With the -m/--merge option, the update is allowed even if the
7637 requested changeset is not an ancestor or descendant of
7640 requested changeset is not an ancestor or descendant of
7638 the working directory's parent.
7641 the working directory's parent.
7639
7642
7640 3. With the -c/--check option, the update is aborted and the
7643 3. With the -c/--check option, the update is aborted and the
7641 uncommitted changes are preserved.
7644 uncommitted changes are preserved.
7642
7645
7643 4. With the -C/--clean option, uncommitted changes are discarded and
7646 4. With the -C/--clean option, uncommitted changes are discarded and
7644 the working directory is updated to the requested changeset.
7647 the working directory is updated to the requested changeset.
7645
7648
7646 To cancel an uncommitted merge (and lose your changes), use
7649 To cancel an uncommitted merge (and lose your changes), use
7647 :hg:`merge --abort`.
7650 :hg:`merge --abort`.
7648
7651
7649 Use null as the changeset to remove the working directory (like
7652 Use null as the changeset to remove the working directory (like
7650 :hg:`clone -U`).
7653 :hg:`clone -U`).
7651
7654
7652 If you want to revert just one file to an older revision, use
7655 If you want to revert just one file to an older revision, use
7653 :hg:`revert [-r REV] NAME`.
7656 :hg:`revert [-r REV] NAME`.
7654
7657
7655 See :hg:`help dates` for a list of formats valid for -d/--date.
7658 See :hg:`help dates` for a list of formats valid for -d/--date.
7656
7659
7657 Returns 0 on success, 1 if there are unresolved files.
7660 Returns 0 on success, 1 if there are unresolved files.
7658 """
7661 """
7659 rev = opts.get('rev')
7662 rev = opts.get('rev')
7660 date = opts.get('date')
7663 date = opts.get('date')
7661 clean = opts.get('clean')
7664 clean = opts.get('clean')
7662 check = opts.get('check')
7665 check = opts.get('check')
7663 merge = opts.get('merge')
7666 merge = opts.get('merge')
7664 if rev and node:
7667 if rev and node:
7665 raise error.Abort(_(b"please specify just one revision"))
7668 raise error.Abort(_(b"please specify just one revision"))
7666
7669
7667 if ui.configbool(b'commands', b'update.requiredest'):
7670 if ui.configbool(b'commands', b'update.requiredest'):
7668 if not node and not rev and not date:
7671 if not node and not rev and not date:
7669 raise error.Abort(
7672 raise error.Abort(
7670 _(b'you must specify a destination'),
7673 _(b'you must specify a destination'),
7671 hint=_(b'for example: hg update ".::"'),
7674 hint=_(b'for example: hg update ".::"'),
7672 )
7675 )
7673
7676
7674 if rev is None or rev == b'':
7677 if rev is None or rev == b'':
7675 rev = node
7678 rev = node
7676
7679
7677 if date and rev is not None:
7680 if date and rev is not None:
7678 raise error.Abort(_(b"you can't specify a revision and a date"))
7681 raise error.Abort(_(b"you can't specify a revision and a date"))
7679
7682
7680 if len([x for x in (clean, check, merge) if x]) > 1:
7683 if len([x for x in (clean, check, merge) if x]) > 1:
7681 raise error.Abort(
7684 raise error.Abort(
7682 _(
7685 _(
7683 b"can only specify one of -C/--clean, -c/--check, "
7686 b"can only specify one of -C/--clean, -c/--check, "
7684 b"or -m/--merge"
7687 b"or -m/--merge"
7685 )
7688 )
7686 )
7689 )
7687
7690
7688 updatecheck = None
7691 updatecheck = None
7689 if check:
7692 if check:
7690 updatecheck = b'abort'
7693 updatecheck = b'abort'
7691 elif merge:
7694 elif merge:
7692 updatecheck = b'none'
7695 updatecheck = b'none'
7693
7696
7694 with repo.wlock():
7697 with repo.wlock():
7695 cmdutil.clearunfinished(repo)
7698 cmdutil.clearunfinished(repo)
7696 if date:
7699 if date:
7697 rev = cmdutil.finddate(ui, repo, date)
7700 rev = cmdutil.finddate(ui, repo, date)
7698
7701
7699 # if we defined a bookmark, we have to remember the original name
7702 # if we defined a bookmark, we have to remember the original name
7700 brev = rev
7703 brev = rev
7701 if rev:
7704 if rev:
7702 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7705 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7703 ctx = scmutil.revsingle(repo, rev, default=None)
7706 ctx = scmutil.revsingle(repo, rev, default=None)
7704 rev = ctx.rev()
7707 rev = ctx.rev()
7705 hidden = ctx.hidden()
7708 hidden = ctx.hidden()
7706 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7709 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7707 with ui.configoverride(overrides, b'update'):
7710 with ui.configoverride(overrides, b'update'):
7708 ret = hg.updatetotally(
7711 ret = hg.updatetotally(
7709 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7712 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7710 )
7713 )
7711 if hidden:
7714 if hidden:
7712 ctxstr = ctx.hex()[:12]
7715 ctxstr = ctx.hex()[:12]
7713 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7716 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7714
7717
7715 if ctx.obsolete():
7718 if ctx.obsolete():
7716 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7719 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7717 ui.warn(b"(%s)\n" % obsfatemsg)
7720 ui.warn(b"(%s)\n" % obsfatemsg)
7718 return ret
7721 return ret
7719
7722
7720
7723
7721 @command(
7724 @command(
7722 b'verify',
7725 b'verify',
7723 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7726 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7724 helpcategory=command.CATEGORY_MAINTENANCE,
7727 helpcategory=command.CATEGORY_MAINTENANCE,
7725 )
7728 )
7726 def verify(ui, repo, **opts):
7729 def verify(ui, repo, **opts):
7727 """verify the integrity of the repository
7730 """verify the integrity of the repository
7728
7731
7729 Verify the integrity of the current repository.
7732 Verify the integrity of the current repository.
7730
7733
7731 This will perform an extensive check of the repository's
7734 This will perform an extensive check of the repository's
7732 integrity, validating the hashes and checksums of each entry in
7735 integrity, validating the hashes and checksums of each entry in
7733 the changelog, manifest, and tracked files, as well as the
7736 the changelog, manifest, and tracked files, as well as the
7734 integrity of their crosslinks and indices.
7737 integrity of their crosslinks and indices.
7735
7738
7736 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7739 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7737 for more information about recovery from corruption of the
7740 for more information about recovery from corruption of the
7738 repository.
7741 repository.
7739
7742
7740 Returns 0 on success, 1 if errors are encountered.
7743 Returns 0 on success, 1 if errors are encountered.
7741 """
7744 """
7742 opts = pycompat.byteskwargs(opts)
7745 opts = pycompat.byteskwargs(opts)
7743
7746
7744 level = None
7747 level = None
7745 if opts[b'full']:
7748 if opts[b'full']:
7746 level = verifymod.VERIFY_FULL
7749 level = verifymod.VERIFY_FULL
7747 return hg.verify(repo, level)
7750 return hg.verify(repo, level)
7748
7751
7749
7752
7750 @command(
7753 @command(
7751 b'version',
7754 b'version',
7752 [] + formatteropts,
7755 [] + formatteropts,
7753 helpcategory=command.CATEGORY_HELP,
7756 helpcategory=command.CATEGORY_HELP,
7754 norepo=True,
7757 norepo=True,
7755 intents={INTENT_READONLY},
7758 intents={INTENT_READONLY},
7756 )
7759 )
7757 def version_(ui, **opts):
7760 def version_(ui, **opts):
7758 """output version and copyright information
7761 """output version and copyright information
7759
7762
7760 .. container:: verbose
7763 .. container:: verbose
7761
7764
7762 Template:
7765 Template:
7763
7766
7764 The following keywords are supported. See also :hg:`help templates`.
7767 The following keywords are supported. See also :hg:`help templates`.
7765
7768
7766 :extensions: List of extensions.
7769 :extensions: List of extensions.
7767 :ver: String. Version number.
7770 :ver: String. Version number.
7768
7771
7769 And each entry of ``{extensions}`` provides the following sub-keywords
7772 And each entry of ``{extensions}`` provides the following sub-keywords
7770 in addition to ``{ver}``.
7773 in addition to ``{ver}``.
7771
7774
7772 :bundled: Boolean. True if included in the release.
7775 :bundled: Boolean. True if included in the release.
7773 :name: String. Extension name.
7776 :name: String. Extension name.
7774 """
7777 """
7775 opts = pycompat.byteskwargs(opts)
7778 opts = pycompat.byteskwargs(opts)
7776 if ui.verbose:
7779 if ui.verbose:
7777 ui.pager(b'version')
7780 ui.pager(b'version')
7778 fm = ui.formatter(b"version", opts)
7781 fm = ui.formatter(b"version", opts)
7779 fm.startitem()
7782 fm.startitem()
7780 fm.write(
7783 fm.write(
7781 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7784 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7782 )
7785 )
7783 license = _(
7786 license = _(
7784 b"(see https://mercurial-scm.org for more information)\n"
7787 b"(see https://mercurial-scm.org for more information)\n"
7785 b"\nCopyright (C) 2005-2019 Matt Mackall and others\n"
7788 b"\nCopyright (C) 2005-2019 Matt Mackall and others\n"
7786 b"This is free software; see the source for copying conditions. "
7789 b"This is free software; see the source for copying conditions. "
7787 b"There is NO\nwarranty; "
7790 b"There is NO\nwarranty; "
7788 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7791 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7789 )
7792 )
7790 if not ui.quiet:
7793 if not ui.quiet:
7791 fm.plain(license)
7794 fm.plain(license)
7792
7795
7793 if ui.verbose:
7796 if ui.verbose:
7794 fm.plain(_(b"\nEnabled extensions:\n\n"))
7797 fm.plain(_(b"\nEnabled extensions:\n\n"))
7795 # format names and versions into columns
7798 # format names and versions into columns
7796 names = []
7799 names = []
7797 vers = []
7800 vers = []
7798 isinternals = []
7801 isinternals = []
7799 for name, module in extensions.extensions():
7802 for name, module in extensions.extensions():
7800 names.append(name)
7803 names.append(name)
7801 vers.append(extensions.moduleversion(module) or None)
7804 vers.append(extensions.moduleversion(module) or None)
7802 isinternals.append(extensions.ismoduleinternal(module))
7805 isinternals.append(extensions.ismoduleinternal(module))
7803 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7806 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7804 if names:
7807 if names:
7805 namefmt = b" %%-%ds " % max(len(n) for n in names)
7808 namefmt = b" %%-%ds " % max(len(n) for n in names)
7806 places = [_(b"external"), _(b"internal")]
7809 places = [_(b"external"), _(b"internal")]
7807 for n, v, p in zip(names, vers, isinternals):
7810 for n, v, p in zip(names, vers, isinternals):
7808 fn.startitem()
7811 fn.startitem()
7809 fn.condwrite(ui.verbose, b"name", namefmt, n)
7812 fn.condwrite(ui.verbose, b"name", namefmt, n)
7810 if ui.verbose:
7813 if ui.verbose:
7811 fn.plain(b"%s " % places[p])
7814 fn.plain(b"%s " % places[p])
7812 fn.data(bundled=p)
7815 fn.data(bundled=p)
7813 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7816 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7814 if ui.verbose:
7817 if ui.verbose:
7815 fn.plain(b"\n")
7818 fn.plain(b"\n")
7816 fn.end()
7819 fn.end()
7817 fm.end()
7820 fm.end()
7818
7821
7819
7822
7820 def loadcmdtable(ui, name, cmdtable):
7823 def loadcmdtable(ui, name, cmdtable):
7821 """Load command functions from specified cmdtable
7824 """Load command functions from specified cmdtable
7822 """
7825 """
7823 overrides = [cmd for cmd in cmdtable if cmd in table]
7826 overrides = [cmd for cmd in cmdtable if cmd in table]
7824 if overrides:
7827 if overrides:
7825 ui.warn(
7828 ui.warn(
7826 _(b"extension '%s' overrides commands: %s\n")
7829 _(b"extension '%s' overrides commands: %s\n")
7827 % (name, b" ".join(overrides))
7830 % (name, b" ".join(overrides))
7828 )
7831 )
7829 table.update(cmdtable)
7832 table.update(cmdtable)
@@ -1,425 +1,425 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 abort
3 abort
4 add
4 add
5 addremove
5 addremove
6 annotate
6 annotate
7 archive
7 archive
8 backout
8 backout
9 bisect
9 bisect
10 bookmarks
10 bookmarks
11 branch
11 branch
12 branches
12 branches
13 bundle
13 bundle
14 cat
14 cat
15 clone
15 clone
16 commit
16 commit
17 config
17 config
18 continue
18 continue
19 copy
19 copy
20 diff
20 diff
21 export
21 export
22 files
22 files
23 forget
23 forget
24 graft
24 graft
25 grep
25 grep
26 heads
26 heads
27 help
27 help
28 identify
28 identify
29 import
29 import
30 incoming
30 incoming
31 init
31 init
32 locate
32 locate
33 log
33 log
34 manifest
34 manifest
35 merge
35 merge
36 outgoing
36 outgoing
37 parents
37 parents
38 paths
38 paths
39 phase
39 phase
40 pull
40 pull
41 push
41 push
42 recover
42 recover
43 remove
43 remove
44 rename
44 rename
45 resolve
45 resolve
46 revert
46 revert
47 rollback
47 rollback
48 root
48 root
49 serve
49 serve
50 shelve
50 shelve
51 status
51 status
52 summary
52 summary
53 tag
53 tag
54 tags
54 tags
55 tip
55 tip
56 unbundle
56 unbundle
57 unshelve
57 unshelve
58 update
58 update
59 verify
59 verify
60 version
60 version
61
61
62 Show all commands that start with "a"
62 Show all commands that start with "a"
63 $ hg debugcomplete a
63 $ hg debugcomplete a
64 abort
64 abort
65 add
65 add
66 addremove
66 addremove
67 annotate
67 annotate
68 archive
68 archive
69
69
70 Do not show debug commands if there are other candidates
70 Do not show debug commands if there are other candidates
71 $ hg debugcomplete d
71 $ hg debugcomplete d
72 diff
72 diff
73
73
74 Show debug commands if there are no other candidates
74 Show debug commands if there are no other candidates
75 $ hg debugcomplete debug
75 $ hg debugcomplete debug
76 debugancestor
76 debugancestor
77 debugapplystreamclonebundle
77 debugapplystreamclonebundle
78 debugbuilddag
78 debugbuilddag
79 debugbundle
79 debugbundle
80 debugcapabilities
80 debugcapabilities
81 debugcheckstate
81 debugcheckstate
82 debugcolor
82 debugcolor
83 debugcommands
83 debugcommands
84 debugcomplete
84 debugcomplete
85 debugconfig
85 debugconfig
86 debugcreatestreamclonebundle
86 debugcreatestreamclonebundle
87 debugdag
87 debugdag
88 debugdata
88 debugdata
89 debugdate
89 debugdate
90 debugdeltachain
90 debugdeltachain
91 debugdirstate
91 debugdirstate
92 debugdiscovery
92 debugdiscovery
93 debugdownload
93 debugdownload
94 debugextensions
94 debugextensions
95 debugfileset
95 debugfileset
96 debugformat
96 debugformat
97 debugfsinfo
97 debugfsinfo
98 debuggetbundle
98 debuggetbundle
99 debugignore
99 debugignore
100 debugindex
100 debugindex
101 debugindexdot
101 debugindexdot
102 debugindexstats
102 debugindexstats
103 debuginstall
103 debuginstall
104 debugknown
104 debugknown
105 debuglabelcomplete
105 debuglabelcomplete
106 debuglocks
106 debuglocks
107 debugmanifestfulltextcache
107 debugmanifestfulltextcache
108 debugmergestate
108 debugmergestate
109 debugnamecomplete
109 debugnamecomplete
110 debugobsolete
110 debugobsolete
111 debugp1copies
111 debugp1copies
112 debugp2copies
112 debugp2copies
113 debugpathcomplete
113 debugpathcomplete
114 debugpathcopies
114 debugpathcopies
115 debugpeer
115 debugpeer
116 debugpickmergetool
116 debugpickmergetool
117 debugpushkey
117 debugpushkey
118 debugpvec
118 debugpvec
119 debugrebuilddirstate
119 debugrebuilddirstate
120 debugrebuildfncache
120 debugrebuildfncache
121 debugrename
121 debugrename
122 debugrevlog
122 debugrevlog
123 debugrevlogindex
123 debugrevlogindex
124 debugrevspec
124 debugrevspec
125 debugserve
125 debugserve
126 debugsetparents
126 debugsetparents
127 debugsidedata
127 debugsidedata
128 debugssl
128 debugssl
129 debugsub
129 debugsub
130 debugsuccessorssets
130 debugsuccessorssets
131 debugtemplate
131 debugtemplate
132 debuguigetpass
132 debuguigetpass
133 debuguiprompt
133 debuguiprompt
134 debugupdatecaches
134 debugupdatecaches
135 debugupgraderepo
135 debugupgraderepo
136 debugwalk
136 debugwalk
137 debugwhyunstable
137 debugwhyunstable
138 debugwireargs
138 debugwireargs
139 debugwireproto
139 debugwireproto
140
140
141 Do not show the alias of a debug command if there are other candidates
141 Do not show the alias of a debug command if there are other candidates
142 (this should hide rawcommit)
142 (this should hide rawcommit)
143 $ hg debugcomplete r
143 $ hg debugcomplete r
144 recover
144 recover
145 remove
145 remove
146 rename
146 rename
147 resolve
147 resolve
148 revert
148 revert
149 rollback
149 rollback
150 root
150 root
151 Show the alias of a debug command if there are no other candidates
151 Show the alias of a debug command if there are no other candidates
152 $ hg debugcomplete rawc
152 $ hg debugcomplete rawc
153
153
154
154
155 Show the global options
155 Show the global options
156 $ hg debugcomplete --options | sort
156 $ hg debugcomplete --options | sort
157 --color
157 --color
158 --config
158 --config
159 --cwd
159 --cwd
160 --debug
160 --debug
161 --debugger
161 --debugger
162 --encoding
162 --encoding
163 --encodingmode
163 --encodingmode
164 --help
164 --help
165 --hidden
165 --hidden
166 --noninteractive
166 --noninteractive
167 --pager
167 --pager
168 --profile
168 --profile
169 --quiet
169 --quiet
170 --repository
170 --repository
171 --time
171 --time
172 --traceback
172 --traceback
173 --verbose
173 --verbose
174 --version
174 --version
175 -R
175 -R
176 -h
176 -h
177 -q
177 -q
178 -v
178 -v
179 -y
179 -y
180
180
181 Show the options for the "serve" command
181 Show the options for the "serve" command
182 $ hg debugcomplete --options serve | sort
182 $ hg debugcomplete --options serve | sort
183 --accesslog
183 --accesslog
184 --address
184 --address
185 --certificate
185 --certificate
186 --cmdserver
186 --cmdserver
187 --color
187 --color
188 --config
188 --config
189 --cwd
189 --cwd
190 --daemon
190 --daemon
191 --daemon-postexec
191 --daemon-postexec
192 --debug
192 --debug
193 --debugger
193 --debugger
194 --encoding
194 --encoding
195 --encodingmode
195 --encodingmode
196 --errorlog
196 --errorlog
197 --help
197 --help
198 --hidden
198 --hidden
199 --ipv6
199 --ipv6
200 --name
200 --name
201 --noninteractive
201 --noninteractive
202 --pager
202 --pager
203 --pid-file
203 --pid-file
204 --port
204 --port
205 --prefix
205 --prefix
206 --print-url
206 --print-url
207 --profile
207 --profile
208 --quiet
208 --quiet
209 --repository
209 --repository
210 --stdio
210 --stdio
211 --style
211 --style
212 --subrepos
212 --subrepos
213 --templates
213 --templates
214 --time
214 --time
215 --traceback
215 --traceback
216 --verbose
216 --verbose
217 --version
217 --version
218 --web-conf
218 --web-conf
219 -6
219 -6
220 -A
220 -A
221 -E
221 -E
222 -R
222 -R
223 -S
223 -S
224 -a
224 -a
225 -d
225 -d
226 -h
226 -h
227 -n
227 -n
228 -p
228 -p
229 -q
229 -q
230 -t
230 -t
231 -v
231 -v
232 -y
232 -y
233
233
234 Show an error if we use --options with an ambiguous abbreviation
234 Show an error if we use --options with an ambiguous abbreviation
235 $ hg debugcomplete --options s
235 $ hg debugcomplete --options s
236 hg: command 's' is ambiguous:
236 hg: command 's' is ambiguous:
237 serve shelve showconfig status summary
237 serve shelve showconfig status summary
238 [255]
238 [255]
239
239
240 Show all commands + options
240 Show all commands + options
241 $ hg debugcommands
241 $ hg debugcommands
242 abort: dry-run
242 abort: dry-run
243 add: include, exclude, subrepos, dry-run
243 add: include, exclude, subrepos, dry-run
244 addremove: similarity, subrepos, include, exclude, dry-run
244 addremove: similarity, subrepos, include, exclude, dry-run
245 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
245 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
246 archive: no-decode, prefix, rev, type, subrepos, include, exclude
246 archive: no-decode, prefix, rev, type, subrepos, include, exclude
247 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
247 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
248 bisect: reset, good, bad, skip, extend, command, noupdate
248 bisect: reset, good, bad, skip, extend, command, noupdate
249 bookmarks: force, rev, delete, rename, inactive, list, template
249 bookmarks: force, rev, delete, rename, inactive, list, template
250 branch: force, clean, rev
250 branch: force, clean, rev
251 branches: active, closed, rev, template
251 branches: active, closed, rev, template
252 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
252 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
253 cat: output, rev, decode, include, exclude, template
253 cat: output, rev, decode, include, exclude, template
254 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
254 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
255 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
255 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
256 config: untrusted, edit, local, global, template
256 config: untrusted, edit, local, global, template
257 continue: dry-run
257 continue: dry-run
258 copy: after, force, include, exclude, dry-run
258 copy: after, force, include, exclude, dry-run
259 debugancestor:
259 debugancestor:
260 debugapplystreamclonebundle:
260 debugapplystreamclonebundle:
261 debugbuilddag: mergeable-file, overwritten-file, new-file
261 debugbuilddag: mergeable-file, overwritten-file, new-file
262 debugbundle: all, part-type, spec
262 debugbundle: all, part-type, spec
263 debugcapabilities:
263 debugcapabilities:
264 debugcheckstate:
264 debugcheckstate:
265 debugcolor: style
265 debugcolor: style
266 debugcommands:
266 debugcommands:
267 debugcomplete: options
267 debugcomplete: options
268 debugcreatestreamclonebundle:
268 debugcreatestreamclonebundle:
269 debugdag: tags, branches, dots, spaces
269 debugdag: tags, branches, dots, spaces
270 debugdata: changelog, manifest, dir
270 debugdata: changelog, manifest, dir
271 debugdate: extended
271 debugdate: extended
272 debugdeltachain: changelog, manifest, dir, template
272 debugdeltachain: changelog, manifest, dir, template
273 debugdirstate: nodates, dates, datesort
273 debugdirstate: nodates, dates, datesort
274 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
274 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
275 debugdownload: output
275 debugdownload: output
276 debugextensions: template
276 debugextensions: template
277 debugfileset: rev, all-files, show-matcher, show-stage
277 debugfileset: rev, all-files, show-matcher, show-stage
278 debugformat: template
278 debugformat: template
279 debugfsinfo:
279 debugfsinfo:
280 debuggetbundle: head, common, type
280 debuggetbundle: head, common, type
281 debugignore:
281 debugignore:
282 debugindex: changelog, manifest, dir, template
282 debugindex: changelog, manifest, dir, template
283 debugindexdot: changelog, manifest, dir
283 debugindexdot: changelog, manifest, dir
284 debugindexstats:
284 debugindexstats:
285 debuginstall: template
285 debuginstall: template
286 debugknown:
286 debugknown:
287 debuglabelcomplete:
287 debuglabelcomplete:
288 debuglocks: force-lock, force-wlock, set-lock, set-wlock
288 debuglocks: force-lock, force-wlock, set-lock, set-wlock
289 debugmanifestfulltextcache: clear, add
289 debugmanifestfulltextcache: clear, add
290 debugmergestate:
290 debugmergestate:
291 debugnamecomplete:
291 debugnamecomplete:
292 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
292 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
293 debugp1copies: rev
293 debugp1copies: rev
294 debugp2copies: rev
294 debugp2copies: rev
295 debugpathcomplete: full, normal, added, removed
295 debugpathcomplete: full, normal, added, removed
296 debugpathcopies: include, exclude
296 debugpathcopies: include, exclude
297 debugpeer:
297 debugpeer:
298 debugpickmergetool: rev, changedelete, include, exclude, tool
298 debugpickmergetool: rev, changedelete, include, exclude, tool
299 debugpushkey:
299 debugpushkey:
300 debugpvec:
300 debugpvec:
301 debugrebuilddirstate: rev, minimal
301 debugrebuilddirstate: rev, minimal
302 debugrebuildfncache:
302 debugrebuildfncache:
303 debugrename: rev
303 debugrename: rev
304 debugrevlog: changelog, manifest, dir, dump
304 debugrevlog: changelog, manifest, dir, dump
305 debugrevlogindex: changelog, manifest, dir, format
305 debugrevlogindex: changelog, manifest, dir, format
306 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
306 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
307 debugserve: sshstdio, logiofd, logiofile
307 debugserve: sshstdio, logiofd, logiofile
308 debugsetparents:
308 debugsetparents:
309 debugsidedata: changelog, manifest, dir
309 debugsidedata: changelog, manifest, dir
310 debugssl:
310 debugssl:
311 debugsub: rev
311 debugsub: rev
312 debugsuccessorssets: closest
312 debugsuccessorssets: closest
313 debugtemplate: rev, define
313 debugtemplate: rev, define
314 debuguigetpass: prompt
314 debuguigetpass: prompt
315 debuguiprompt: prompt
315 debuguiprompt: prompt
316 debugupdatecaches:
316 debugupdatecaches:
317 debugupgraderepo: optimize, run, backup, changelog, manifest
317 debugupgraderepo: optimize, run, backup, changelog, manifest
318 debugwalk: include, exclude
318 debugwalk: include, exclude
319 debugwhyunstable:
319 debugwhyunstable:
320 debugwireargs: three, four, five, ssh, remotecmd, insecure
320 debugwireargs: three, four, five, ssh, remotecmd, insecure
321 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
321 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
322 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
322 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
323 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
323 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
324 files: rev, print0, include, exclude, template, subrepos
324 files: rev, print0, include, exclude, template, subrepos
325 forget: interactive, include, exclude, dry-run
325 forget: interactive, include, exclude, dry-run
326 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
326 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
327 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
327 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
328 heads: rev, topo, active, closed, style, template
328 heads: rev, topo, active, closed, style, template
329 help: extension, command, keyword, system
329 help: extension, command, keyword, system
330 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
330 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
331 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
331 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
332 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
332 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
333 init: ssh, remotecmd, insecure
333 init: ssh, remotecmd, insecure
334 locate: rev, print0, fullpath, include, exclude
334 locate: rev, print0, fullpath, include, exclude
335 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
335 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
336 manifest: rev, all, template
336 manifest: rev, all, template
337 merge: force, rev, preview, abort, tool
337 merge: force, rev, preview, abort, tool
338 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
338 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
339 parents: rev, style, template
339 parents: rev, style, template
340 paths: template
340 paths: template
341 phase: public, draft, secret, force, rev
341 phase: public, draft, secret, force, rev
342 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
342 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
343 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
343 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
344 recover: verify
344 recover: verify
345 remove: after, force, subrepos, include, exclude, dry-run
345 remove: after, force, subrepos, include, exclude, dry-run
346 rename: after, force, include, exclude, dry-run
346 rename: after, force, include, exclude, dry-run
347 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
347 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
348 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
348 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
349 rollback: dry-run, force
349 rollback: dry-run, force
350 root: template
350 root: template
351 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
351 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
352 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
352 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
353 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
353 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
354 summary: remote
354 summary: remote
355 tag: force, local, rev, remove, edit, message, date, user
355 tag: force, local, rev, remove, edit, message, date, user
356 tags: template
356 tags: template
357 tip: patch, git, style, template
357 tip: patch, git, style, template
358 unbundle: update
358 unbundle: update
359 unshelve: abort, continue, interactive, keep, name, tool, date
359 unshelve: abort, continue, interactive, keep, name, tool, date
360 update: clean, check, merge, date, rev, tool
360 update: clean, check, merge, date, rev, tool
361 verify: full
361 verify: full
362 version: template
362 version: template
363
363
364 $ hg init a
364 $ hg init a
365 $ cd a
365 $ cd a
366 $ echo fee > fee
366 $ echo fee > fee
367 $ hg ci -q -Amfee
367 $ hg ci -q -Amfee
368 $ hg tag fee
368 $ hg tag fee
369 $ mkdir fie
369 $ mkdir fie
370 $ echo dead > fie/dead
370 $ echo dead > fie/dead
371 $ echo live > fie/live
371 $ echo live > fie/live
372 $ hg bookmark fo
372 $ hg bookmark fo
373 $ hg branch -q fie
373 $ hg branch -q fie
374 $ hg ci -q -Amfie
374 $ hg ci -q -Amfie
375 $ echo fo > fo
375 $ echo fo > fo
376 $ hg branch -qf default
376 $ hg branch -qf default
377 $ hg ci -q -Amfo
377 $ hg ci -q -Amfo
378 $ echo Fum > Fum
378 $ echo Fum > Fum
379 $ hg ci -q -AmFum
379 $ hg ci -q -AmFum
380 $ hg bookmark Fum
380 $ hg bookmark Fum
381
381
382 Test debugpathcomplete
382 Test debugpathcomplete
383
383
384 $ hg debugpathcomplete f
384 $ hg debugpathcomplete f
385 fee
385 fee
386 fie
386 fie
387 fo
387 fo
388 $ hg debugpathcomplete -f f
388 $ hg debugpathcomplete -f f
389 fee
389 fee
390 fie/dead
390 fie/dead
391 fie/live
391 fie/live
392 fo
392 fo
393
393
394 $ hg rm Fum
394 $ hg rm Fum
395 $ hg debugpathcomplete -r F
395 $ hg debugpathcomplete -r F
396 Fum
396 Fum
397
397
398 Test debugnamecomplete
398 Test debugnamecomplete
399
399
400 $ hg debugnamecomplete
400 $ hg debugnamecomplete
401 Fum
401 Fum
402 default
402 default
403 fee
403 fee
404 fie
404 fie
405 fo
405 fo
406 tip
406 tip
407 $ hg debugnamecomplete f
407 $ hg debugnamecomplete f
408 fee
408 fee
409 fie
409 fie
410 fo
410 fo
411
411
412 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
412 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
413 used for completions in some shells.
413 used for completions in some shells.
414
414
415 $ hg debuglabelcomplete
415 $ hg debuglabelcomplete
416 Fum
416 Fum
417 default
417 default
418 fee
418 fee
419 fie
419 fie
420 fo
420 fo
421 tip
421 tip
422 $ hg debuglabelcomplete f
422 $ hg debuglabelcomplete f
423 fee
423 fee
424 fie
424 fie
425 fo
425 fo
@@ -1,2038 +1,2081 b''
1 $ hg init a
1 $ hg init a
2 $ mkdir a/d1
2 $ mkdir a/d1
3 $ mkdir a/d1/d2
3 $ mkdir a/d1/d2
4 $ echo line 1 > a/a
4 $ echo line 1 > a/a
5 $ echo line 1 > a/d1/d2/a
5 $ echo line 1 > a/d1/d2/a
6 $ hg --cwd a ci -Ama
6 $ hg --cwd a ci -Ama
7 adding a
7 adding a
8 adding d1/d2/a
8 adding d1/d2/a
9
9
10 $ echo line 2 >> a/a
10 $ echo line 2 >> a/a
11 $ hg --cwd a ci -u someone -d '1 0' -m'second change'
11 $ hg --cwd a ci -u someone -d '1 0' -m'second change'
12
12
13 import with no args:
13 import with no args:
14
14
15 $ hg --cwd a import
15 $ hg --cwd a import
16 abort: need at least one patch to import
16 abort: need at least one patch to import
17 [255]
17 [255]
18
18
19 generate patches for the test
19 generate patches for the test
20
20
21 $ hg --cwd a export tip > exported-tip.patch
21 $ hg --cwd a export tip > exported-tip.patch
22 $ hg --cwd a diff -r0:1 > diffed-tip.patch
22 $ hg --cwd a diff -r0:1 > diffed-tip.patch
23
23
24
24
25 import exported patch
25 import exported patch
26 (this also tests that editor is not invoked, if the patch contains the
26 (this also tests that editor is not invoked, if the patch contains the
27 commit message and '--edit' is not specified)
27 commit message and '--edit' is not specified)
28
28
29 $ hg clone -r0 a b
29 $ hg clone -r0 a b
30 adding changesets
30 adding changesets
31 adding manifests
31 adding manifests
32 adding file changes
32 adding file changes
33 added 1 changesets with 2 changes to 2 files
33 added 1 changesets with 2 changes to 2 files
34 new changesets 80971e65b431
34 new changesets 80971e65b431
35 updating to branch default
35 updating to branch default
36 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 $ HGEDITOR=cat hg --cwd b import --debug ../exported-tip.patch
37 $ HGEDITOR=cat hg --cwd b import --debug ../exported-tip.patch
38 applying ../exported-tip.patch
38 applying ../exported-tip.patch
39 Subject:
39 Subject:
40
40
41 Content-Type: text/plain
41 Content-Type: text/plain
42 found patch at byte 202
42 found patch at byte 202
43 patch generated by hg export
43 patch generated by hg export
44 From: someone
44 From: someone
45 Date: 1 0
45 Date: 1 0
46 Node ID: 1d4bd90af0e43687763d158dfa83ff2a4b6c0c32
46 Node ID: 1d4bd90af0e43687763d158dfa83ff2a4b6c0c32
47 message:
47 message:
48 second change
48 second change
49 patching file a
49 patching file a
50 committing files:
50 committing files:
51 a
51 a
52 committing manifest
52 committing manifest
53 committing changelog
53 committing changelog
54 created 1d4bd90af0e4
54 created 1d4bd90af0e4
55 updating the branch cache
55 updating the branch cache
56
56
57 message and committer and date should be same
57 message and committer and date should be same
58
58
59 $ hg --cwd b tip
59 $ hg --cwd b tip
60 changeset: 1:1d4bd90af0e4
60 changeset: 1:1d4bd90af0e4
61 tag: tip
61 tag: tip
62 user: someone
62 user: someone
63 date: Thu Jan 01 00:00:01 1970 +0000
63 date: Thu Jan 01 00:00:01 1970 +0000
64 summary: second change
64 summary: second change
65
65
66 $ rm -r b
66 $ rm -r b
67
67
68
68
69 import exported patch with external patcher
69 import exported patch with external patcher
70 (this also tests that editor is invoked, if the '--edit' is specified,
70 (this also tests that editor is invoked, if the '--edit' is specified,
71 regardless of the commit message in the patch)
71 regardless of the commit message in the patch)
72
72
73 $ cat > dummypatch.py <<EOF
73 $ cat > dummypatch.py <<EOF
74 > from __future__ import print_function
74 > from __future__ import print_function
75 > print('patching file a')
75 > print('patching file a')
76 > open('a', 'wb').write(b'line2\n')
76 > open('a', 'wb').write(b'line2\n')
77 > EOF
77 > EOF
78 $ hg clone -r0 a b
78 $ hg clone -r0 a b
79 adding changesets
79 adding changesets
80 adding manifests
80 adding manifests
81 adding file changes
81 adding file changes
82 added 1 changesets with 2 changes to 2 files
82 added 1 changesets with 2 changes to 2 files
83 new changesets 80971e65b431
83 new changesets 80971e65b431
84 updating to branch default
84 updating to branch default
85 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 $ HGEDITOR=cat hg --config ui.patch="\"$PYTHON\" ../dummypatch.py" --cwd b import --edit ../exported-tip.patch
86 $ HGEDITOR=cat hg --config ui.patch="\"$PYTHON\" ../dummypatch.py" --cwd b import --edit ../exported-tip.patch
87 applying ../exported-tip.patch
87 applying ../exported-tip.patch
88 second change
88 second change
89
89
90
90
91 HG: Enter commit message. Lines beginning with 'HG:' are removed.
91 HG: Enter commit message. Lines beginning with 'HG:' are removed.
92 HG: Leave message empty to abort commit.
92 HG: Leave message empty to abort commit.
93 HG: --
93 HG: --
94 HG: user: someone
94 HG: user: someone
95 HG: branch 'default'
95 HG: branch 'default'
96 HG: changed a
96 HG: changed a
97 $ cat b/a
97 $ cat b/a
98 line2
98 line2
99 $ rm -r b
99 $ rm -r b
100
100
101
101
102 import of plain diff should fail without message
102 import of plain diff should fail without message
103 (this also tests that editor is invoked, if the patch doesn't contain
103 (this also tests that editor is invoked, if the patch doesn't contain
104 the commit message, regardless of '--edit')
104 the commit message, regardless of '--edit')
105
105
106 $ hg clone -r0 a b
106 $ hg clone -r0 a b
107 adding changesets
107 adding changesets
108 adding manifests
108 adding manifests
109 adding file changes
109 adding file changes
110 added 1 changesets with 2 changes to 2 files
110 added 1 changesets with 2 changes to 2 files
111 new changesets 80971e65b431
111 new changesets 80971e65b431
112 updating to branch default
112 updating to branch default
113 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 $ cat > $TESTTMP/editor.sh <<EOF
114 $ cat > $TESTTMP/editor.sh <<EOF
115 > env | grep HGEDITFORM
115 > env | grep HGEDITFORM
116 > cat \$1
116 > cat \$1
117 > EOF
117 > EOF
118 $ HGEDITOR="sh $TESTTMP/editor.sh" hg --cwd b import ../diffed-tip.patch
118 $ HGEDITOR="sh $TESTTMP/editor.sh" hg --cwd b import ../diffed-tip.patch
119 applying ../diffed-tip.patch
119 applying ../diffed-tip.patch
120 HGEDITFORM=import.normal.normal
120 HGEDITFORM=import.normal.normal
121
121
122
122
123 HG: Enter commit message. Lines beginning with 'HG:' are removed.
123 HG: Enter commit message. Lines beginning with 'HG:' are removed.
124 HG: Leave message empty to abort commit.
124 HG: Leave message empty to abort commit.
125 HG: --
125 HG: --
126 HG: user: test
126 HG: user: test
127 HG: branch 'default'
127 HG: branch 'default'
128 HG: changed a
128 HG: changed a
129 abort: empty commit message
129 abort: empty commit message
130 [255]
130 [255]
131
131
132 Test avoiding editor invocation at applying the patch with --exact,
132 Test avoiding editor invocation at applying the patch with --exact,
133 even if commit message is empty
133 even if commit message is empty
134
134
135 $ echo a >> b/a
135 $ echo a >> b/a
136 $ hg --cwd b commit -m ' '
136 $ hg --cwd b commit -m ' '
137 $ hg --cwd b tip -T "{node}\n"
137 $ hg --cwd b tip -T "{node}\n"
138 d8804f3f5396d800812f579c8452796a5993bdb2
138 d8804f3f5396d800812f579c8452796a5993bdb2
139 $ hg --cwd b export -o ../empty-log.diff .
139 $ hg --cwd b export -o ../empty-log.diff .
140 $ hg --cwd b update -q -C ".^1"
140 $ hg --cwd b update -q -C ".^1"
141 $ hg --cwd b --config extensions.strip= strip -q tip
141 $ hg --cwd b --config extensions.strip= strip -q tip
142 $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
142 $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
143 applying ../empty-log.diff
143 applying ../empty-log.diff
144 $ hg --cwd b tip -T "{node}\n"
144 $ hg --cwd b tip -T "{node}\n"
145 d8804f3f5396d800812f579c8452796a5993bdb2
145 d8804f3f5396d800812f579c8452796a5993bdb2
146
146
147 $ rm -r b
147 $ rm -r b
148
148
149
149
150 import of plain diff should be ok with message
150 import of plain diff should be ok with message
151
151
152 $ hg clone -r0 a b
152 $ hg clone -r0 a b
153 adding changesets
153 adding changesets
154 adding manifests
154 adding manifests
155 adding file changes
155 adding file changes
156 added 1 changesets with 2 changes to 2 files
156 added 1 changesets with 2 changes to 2 files
157 new changesets 80971e65b431
157 new changesets 80971e65b431
158 updating to branch default
158 updating to branch default
159 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 $ hg --cwd b import -mpatch ../diffed-tip.patch
160 $ hg --cwd b import -mpatch ../diffed-tip.patch
161 applying ../diffed-tip.patch
161 applying ../diffed-tip.patch
162 $ rm -r b
162 $ rm -r b
163
163
164
164
165 import of plain diff with specific date and user
165 import of plain diff with specific date and user
166 (this also tests that editor is not invoked, if
166 (this also tests that editor is not invoked, if
167 '--message'/'--logfile' is specified and '--edit' is not)
167 '--message'/'--logfile' is specified and '--edit' is not)
168
168
169 $ hg clone -r0 a b
169 $ hg clone -r0 a b
170 adding changesets
170 adding changesets
171 adding manifests
171 adding manifests
172 adding file changes
172 adding file changes
173 added 1 changesets with 2 changes to 2 files
173 added 1 changesets with 2 changes to 2 files
174 new changesets 80971e65b431
174 new changesets 80971e65b431
175 updating to branch default
175 updating to branch default
176 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 $ hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../diffed-tip.patch
177 $ hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../diffed-tip.patch
178 applying ../diffed-tip.patch
178 applying ../diffed-tip.patch
179 $ hg -R b tip -pv
179 $ hg -R b tip -pv
180 changeset: 1:ca68f19f3a40
180 changeset: 1:ca68f19f3a40
181 tag: tip
181 tag: tip
182 user: user@nowhere.net
182 user: user@nowhere.net
183 date: Thu Jan 01 00:00:01 1970 +0000
183 date: Thu Jan 01 00:00:01 1970 +0000
184 files: a
184 files: a
185 description:
185 description:
186 patch
186 patch
187
187
188
188
189 diff -r 80971e65b431 -r ca68f19f3a40 a
189 diff -r 80971e65b431 -r ca68f19f3a40 a
190 --- a/a Thu Jan 01 00:00:00 1970 +0000
190 --- a/a Thu Jan 01 00:00:00 1970 +0000
191 +++ b/a Thu Jan 01 00:00:01 1970 +0000
191 +++ b/a Thu Jan 01 00:00:01 1970 +0000
192 @@ -1,1 +1,2 @@
192 @@ -1,1 +1,2 @@
193 line 1
193 line 1
194 +line 2
194 +line 2
195
195
196 $ rm -r b
196 $ rm -r b
197
197
198
198
199 import of plain diff should be ok with --no-commit
199 import of plain diff should be ok with --no-commit
200 (this also tests that editor is not invoked, if '--no-commit' is
200 (this also tests that editor is not invoked, if '--no-commit' is
201 specified, regardless of '--edit')
201 specified, regardless of '--edit')
202
202
203 $ hg clone -r0 a b
203 $ hg clone -r0 a b
204 adding changesets
204 adding changesets
205 adding manifests
205 adding manifests
206 adding file changes
206 adding file changes
207 added 1 changesets with 2 changes to 2 files
207 added 1 changesets with 2 changes to 2 files
208 new changesets 80971e65b431
208 new changesets 80971e65b431
209 updating to branch default
209 updating to branch default
210 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
211 $ HGEDITOR=cat hg --cwd b import --no-commit --edit ../diffed-tip.patch
211 $ HGEDITOR=cat hg --cwd b import --no-commit --edit ../diffed-tip.patch
212 applying ../diffed-tip.patch
212 applying ../diffed-tip.patch
213 $ hg --cwd b diff --nodates
213 $ hg --cwd b diff --nodates
214 diff -r 80971e65b431 a
214 diff -r 80971e65b431 a
215 --- a/a
215 --- a/a
216 +++ b/a
216 +++ b/a
217 @@ -1,1 +1,2 @@
217 @@ -1,1 +1,2 @@
218 line 1
218 line 1
219 +line 2
219 +line 2
220 $ rm -r b
220 $ rm -r b
221
221
222
222
223 import of malformed plain diff should fail
223 import of malformed plain diff should fail
224
224
225 $ hg clone -r0 a b
225 $ hg clone -r0 a b
226 adding changesets
226 adding changesets
227 adding manifests
227 adding manifests
228 adding file changes
228 adding file changes
229 added 1 changesets with 2 changes to 2 files
229 added 1 changesets with 2 changes to 2 files
230 new changesets 80971e65b431
230 new changesets 80971e65b431
231 updating to branch default
231 updating to branch default
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
233 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
234 $ hg --cwd b import -mpatch ../broken.patch
234 $ hg --cwd b import -mpatch ../broken.patch
235 applying ../broken.patch
235 applying ../broken.patch
236 abort: bad hunk #1
236 abort: bad hunk #1
237 [255]
237 [255]
238 $ rm -r b
238 $ rm -r b
239
239
240 hg -R repo import
240 hg -R repo import
241 put the clone in a subdir - having a directory named "a"
241 put the clone in a subdir - having a directory named "a"
242 used to hide a bug.
242 used to hide a bug.
243
243
244 $ mkdir dir
244 $ mkdir dir
245 $ hg clone -r0 a dir/b
245 $ hg clone -r0 a dir/b
246 adding changesets
246 adding changesets
247 adding manifests
247 adding manifests
248 adding file changes
248 adding file changes
249 added 1 changesets with 2 changes to 2 files
249 added 1 changesets with 2 changes to 2 files
250 new changesets 80971e65b431
250 new changesets 80971e65b431
251 updating to branch default
251 updating to branch default
252 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
252 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 $ cd dir
253 $ cd dir
254 $ hg -R b import ../exported-tip.patch
254 $ hg -R b import ../exported-tip.patch
255 applying ../exported-tip.patch
255 applying ../exported-tip.patch
256 $ cd ..
256 $ cd ..
257 $ rm -r dir
257 $ rm -r dir
258
258
259
259
260 import from stdin
260 import from stdin
261
261
262 $ hg clone -r0 a b
262 $ hg clone -r0 a b
263 adding changesets
263 adding changesets
264 adding manifests
264 adding manifests
265 adding file changes
265 adding file changes
266 added 1 changesets with 2 changes to 2 files
266 added 1 changesets with 2 changes to 2 files
267 new changesets 80971e65b431
267 new changesets 80971e65b431
268 updating to branch default
268 updating to branch default
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 $ hg --cwd b import - < exported-tip.patch
270 $ hg --cwd b import - < exported-tip.patch
271 applying patch from stdin
271 applying patch from stdin
272 $ rm -r b
272 $ rm -r b
273
273
274
274
275 import two patches in one stream
275 import two patches in one stream
276
276
277 $ hg init b
277 $ hg init b
278 $ hg --cwd a export 0:tip | hg --cwd b import -
278 $ hg --cwd a export 0:tip | hg --cwd b import -
279 applying patch from stdin
279 applying patch from stdin
280 $ hg --cwd a id
280 $ hg --cwd a id
281 1d4bd90af0e4 tip
281 1d4bd90af0e4 tip
282 $ hg --cwd b id
282 $ hg --cwd b id
283 1d4bd90af0e4 tip
283 1d4bd90af0e4 tip
284 $ rm -r b
284 $ rm -r b
285
285
286
286
287 override commit message
287 override commit message
288
288
289 $ hg clone -r0 a b
289 $ hg clone -r0 a b
290 adding changesets
290 adding changesets
291 adding manifests
291 adding manifests
292 adding file changes
292 adding file changes
293 added 1 changesets with 2 changes to 2 files
293 added 1 changesets with 2 changes to 2 files
294 new changesets 80971e65b431
294 new changesets 80971e65b431
295 updating to branch default
295 updating to branch default
296 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 $ hg --cwd b import -m 'override' - < exported-tip.patch
297 $ hg --cwd b import -m 'override' - < exported-tip.patch
298 applying patch from stdin
298 applying patch from stdin
299 $ hg --cwd b tip | grep override
299 $ hg --cwd b tip | grep override
300 summary: override
300 summary: override
301 $ rm -r b
301 $ rm -r b
302
302
303 $ cat > mkmsg.py <<EOF
303 $ cat > mkmsg.py <<EOF
304 > import email.message
304 > import email.message
305 > import sys
305 > import sys
306 > msg = email.message.Message()
306 > msg = email.message.Message()
307 > patch = open(sys.argv[1], 'rb').read()
307 > patch = open(sys.argv[1], 'rb').read()
308 > msg.set_payload(b'email commit message\n' + patch)
308 > msg.set_payload(b'email commit message\n' + patch)
309 > msg['Subject'] = 'email patch'
309 > msg['Subject'] = 'email patch'
310 > msg['From'] = 'email patcher'
310 > msg['From'] = 'email patcher'
311 > open(sys.argv[2], 'wb').write(bytes(msg))
311 > open(sys.argv[2], 'wb').write(bytes(msg))
312 > EOF
312 > EOF
313
313
314
314
315 plain diff in email, subject, message body
315 plain diff in email, subject, message body
316
316
317 $ hg clone -r0 a b
317 $ hg clone -r0 a b
318 adding changesets
318 adding changesets
319 adding manifests
319 adding manifests
320 adding file changes
320 adding file changes
321 added 1 changesets with 2 changes to 2 files
321 added 1 changesets with 2 changes to 2 files
322 new changesets 80971e65b431
322 new changesets 80971e65b431
323 updating to branch default
323 updating to branch default
324 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
325 $ "$PYTHON" mkmsg.py diffed-tip.patch msg.patch
325 $ "$PYTHON" mkmsg.py diffed-tip.patch msg.patch
326 $ hg --cwd b import ../msg.patch
326 $ hg --cwd b import ../msg.patch
327 applying ../msg.patch
327 applying ../msg.patch
328 $ hg --cwd b tip | grep email
328 $ hg --cwd b tip | grep email
329 user: email patcher
329 user: email patcher
330 summary: email patch
330 summary: email patch
331 $ rm -r b
331 $ rm -r b
332
332
333
333
334 plain diff in email, no subject, message body
334 plain diff in email, no subject, message body
335
335
336 $ hg clone -r0 a b
336 $ hg clone -r0 a b
337 adding changesets
337 adding changesets
338 adding manifests
338 adding manifests
339 adding file changes
339 adding file changes
340 added 1 changesets with 2 changes to 2 files
340 added 1 changesets with 2 changes to 2 files
341 new changesets 80971e65b431
341 new changesets 80971e65b431
342 updating to branch default
342 updating to branch default
343 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
344 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
344 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
345 applying patch from stdin
345 applying patch from stdin
346 $ rm -r b
346 $ rm -r b
347
347
348
348
349 plain diff in email, subject, no message body
349 plain diff in email, subject, no message body
350
350
351 $ hg clone -r0 a b
351 $ hg clone -r0 a b
352 adding changesets
352 adding changesets
353 adding manifests
353 adding manifests
354 adding file changes
354 adding file changes
355 added 1 changesets with 2 changes to 2 files
355 added 1 changesets with 2 changes to 2 files
356 new changesets 80971e65b431
356 new changesets 80971e65b431
357 updating to branch default
357 updating to branch default
358 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 $ grep -v '^email ' msg.patch | hg --cwd b import -
359 $ grep -v '^email ' msg.patch | hg --cwd b import -
360 applying patch from stdin
360 applying patch from stdin
361 $ rm -r b
361 $ rm -r b
362
362
363
363
364 plain diff in email, no subject, no message body, should fail
364 plain diff in email, no subject, no message body, should fail
365
365
366 $ hg clone -r0 a b
366 $ hg clone -r0 a b
367 adding changesets
367 adding changesets
368 adding manifests
368 adding manifests
369 adding file changes
369 adding file changes
370 added 1 changesets with 2 changes to 2 files
370 added 1 changesets with 2 changes to 2 files
371 new changesets 80971e65b431
371 new changesets 80971e65b431
372 updating to branch default
372 updating to branch default
373 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
374 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
374 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
375 applying patch from stdin
375 applying patch from stdin
376 abort: empty commit message
376 abort: empty commit message
377 [255]
377 [255]
378 $ rm -r b
378 $ rm -r b
379
379
380
380
381 hg export in email, should use patch header
381 hg export in email, should use patch header
382
382
383 $ hg clone -r0 a b
383 $ hg clone -r0 a b
384 adding changesets
384 adding changesets
385 adding manifests
385 adding manifests
386 adding file changes
386 adding file changes
387 added 1 changesets with 2 changes to 2 files
387 added 1 changesets with 2 changes to 2 files
388 new changesets 80971e65b431
388 new changesets 80971e65b431
389 updating to branch default
389 updating to branch default
390 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
390 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 $ "$PYTHON" mkmsg.py exported-tip.patch msg.patch
391 $ "$PYTHON" mkmsg.py exported-tip.patch msg.patch
392 $ cat msg.patch | hg --cwd b import -
392 $ cat msg.patch | hg --cwd b import -
393 applying patch from stdin
393 applying patch from stdin
394 $ hg --cwd b tip | grep second
394 $ hg --cwd b tip | grep second
395 summary: second change
395 summary: second change
396 $ rm -r b
396 $ rm -r b
397
397
398 hg email --plain, should read X-Mercurial-Node header
398 hg email --plain, should read X-Mercurial-Node header
399
399
400 $ cat >> a/.hg/hgrc << EOF
400 $ cat >> a/.hg/hgrc << EOF
401 > [extensions]
401 > [extensions]
402 > patchbomb =
402 > patchbomb =
403 > [email]
403 > [email]
404 > from = foo
404 > from = foo
405 > cc = foo
405 > cc = foo
406 > to = bar
406 > to = bar
407 > EOF
407 > EOF
408 $ hg --cwd a email -m ../tip-plain.mbox --plain --date '1970-1-1 0:1' tip
408 $ hg --cwd a email -m ../tip-plain.mbox --plain --date '1970-1-1 0:1' tip
409 this patch series consists of 1 patches.
409 this patch series consists of 1 patches.
410
410
411
411
412 sending [PATCH] second change ...
412 sending [PATCH] second change ...
413
413
414 $ hg clone -r0 a b -q
414 $ hg clone -r0 a b -q
415 $ hg --cwd b import --debug ../tip-plain.mbox
415 $ hg --cwd b import --debug ../tip-plain.mbox
416 applying ../tip-plain.mbox
416 applying ../tip-plain.mbox
417 Node ID: 1d4bd90af0e43687763d158dfa83ff2a4b6c0c32
417 Node ID: 1d4bd90af0e43687763d158dfa83ff2a4b6c0c32
418 Subject: second change
418 Subject: second change
419 From: foo
419 From: foo
420 Content-Type: text/plain
420 Content-Type: text/plain
421 found patch at byte 0
421 found patch at byte 0
422 message:
422 message:
423 second change
423 second change
424 patching file a
424 patching file a
425 committing files:
425 committing files:
426 a
426 a
427 committing manifest
427 committing manifest
428 committing changelog
428 committing changelog
429 created de620f6fe949
429 created de620f6fe949
430 updating the branch cache
430 updating the branch cache
431 $ hg --cwd b tip
431 $ hg --cwd b tip
432 changeset: 1:de620f6fe949
432 changeset: 1:de620f6fe949
433 tag: tip
433 tag: tip
434 user: foo
434 user: foo
435 date: Thu Jan 01 00:00:00 1970 +0000
435 date: Thu Jan 01 00:00:00 1970 +0000
436 summary: second change
436 summary: second change
437
437
438 $ hg --cwd b phase tip
439 1: draft
440 $ rm -r b
441
442
443 hg import --secret
444
445 $ hg clone -r0 a b -q
446 $ hg --cwd b import --no-commit --secret ../exported-tip.patch
447 abort: cannot use --no-commit with --secret
448 [255]
449 $ hg --cwd b import --secret ../exported-tip.patch
450 applying ../exported-tip.patch
451 $ hg --cwd b diff -c . --nodates
452 diff -r 80971e65b431 -r 1d4bd90af0e4 a
453 --- a/a
454 +++ b/a
455 @@ -1,1 +1,2 @@
456 line 1
457 +line 2
458 $ hg --cwd b phase
459 1: secret
460 $ hg --cwd b --config extensions.strip= strip 1 --no-backup --quiet
461 $ HGEDITOR=cat hg --cwd b import --secret --edit ../exported-tip.patch
462 applying ../exported-tip.patch
463 second change
464
465
466 HG: Enter commit message. Lines beginning with 'HG:' are removed.
467 HG: Leave message empty to abort commit.
468 HG: --
469 HG: user: someone
470 HG: branch 'default'
471 HG: changed a
472 $ hg --cwd b diff -c . --nodates
473 diff -r 80971e65b431 -r 1d4bd90af0e4 a
474 --- a/a
475 +++ b/a
476 @@ -1,1 +1,2 @@
477 line 1
478 +line 2
479 $ hg --cwd b phase
480 1: secret
438 $ rm -r b
481 $ rm -r b
439
482
440
483
441 subject: duplicate detection, removal of [PATCH]
484 subject: duplicate detection, removal of [PATCH]
442 The '---' tests the gitsendmail handling without proper mail headers
485 The '---' tests the gitsendmail handling without proper mail headers
443
486
444 $ cat > mkmsg2.py <<EOF
487 $ cat > mkmsg2.py <<EOF
445 > import email.message
488 > import email.message
446 > import sys
489 > import sys
447 > msg = email.message.Message()
490 > msg = email.message.Message()
448 > patch = open(sys.argv[1], 'rb').read()
491 > patch = open(sys.argv[1], 'rb').read()
449 > msg.set_payload(b'email patch\n\nnext line\n---\n' + patch)
492 > msg.set_payload(b'email patch\n\nnext line\n---\n' + patch)
450 > msg['Subject'] = '[PATCH] email patch'
493 > msg['Subject'] = '[PATCH] email patch'
451 > msg['From'] = 'email patcher'
494 > msg['From'] = 'email patcher'
452 > open(sys.argv[2], 'wb').write(bytes(msg))
495 > open(sys.argv[2], 'wb').write(bytes(msg))
453 > EOF
496 > EOF
454
497
455
498
456 plain diff in email, [PATCH] subject, message body with subject
499 plain diff in email, [PATCH] subject, message body with subject
457
500
458 $ hg clone -r0 a b
501 $ hg clone -r0 a b
459 adding changesets
502 adding changesets
460 adding manifests
503 adding manifests
461 adding file changes
504 adding file changes
462 added 1 changesets with 2 changes to 2 files
505 added 1 changesets with 2 changes to 2 files
463 new changesets 80971e65b431
506 new changesets 80971e65b431
464 updating to branch default
507 updating to branch default
465 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
508 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
466 $ "$PYTHON" mkmsg2.py diffed-tip.patch msg.patch
509 $ "$PYTHON" mkmsg2.py diffed-tip.patch msg.patch
467 $ cat msg.patch | hg --cwd b import -
510 $ cat msg.patch | hg --cwd b import -
468 applying patch from stdin
511 applying patch from stdin
469 $ hg --cwd b tip --template '{desc}\n'
512 $ hg --cwd b tip --template '{desc}\n'
470 email patch
513 email patch
471
514
472 next line
515 next line
473 $ rm -r b
516 $ rm -r b
474
517
475
518
476 Issue963: Parent of working dir incorrect after import of multiple
519 Issue963: Parent of working dir incorrect after import of multiple
477 patches and rollback
520 patches and rollback
478
521
479 We weren't backing up the correct dirstate file when importing many
522 We weren't backing up the correct dirstate file when importing many
480 patches: import patch1 patch2; rollback
523 patches: import patch1 patch2; rollback
481
524
482 $ echo line 3 >> a/a
525 $ echo line 3 >> a/a
483 $ hg --cwd a ci -m'third change'
526 $ hg --cwd a ci -m'third change'
484 $ hg --cwd a export -o '../patch%R' 1 2
527 $ hg --cwd a export -o '../patch%R' 1 2
485 $ hg clone -qr0 a b
528 $ hg clone -qr0 a b
486 $ hg --cwd b parents --template 'parent: {rev}\n'
529 $ hg --cwd b parents --template 'parent: {rev}\n'
487 parent: 0
530 parent: 0
488 $ hg --cwd b import -v ../patch1 ../patch2
531 $ hg --cwd b import -v ../patch1 ../patch2
489 applying ../patch1
532 applying ../patch1
490 patching file a
533 patching file a
491 committing files:
534 committing files:
492 a
535 a
493 committing manifest
536 committing manifest
494 committing changelog
537 committing changelog
495 created 1d4bd90af0e4
538 created 1d4bd90af0e4
496 applying ../patch2
539 applying ../patch2
497 patching file a
540 patching file a
498 committing files:
541 committing files:
499 a
542 a
500 committing manifest
543 committing manifest
501 committing changelog
544 committing changelog
502 created 6d019af21222
545 created 6d019af21222
503 $ hg --cwd b rollback
546 $ hg --cwd b rollback
504 repository tip rolled back to revision 0 (undo import)
547 repository tip rolled back to revision 0 (undo import)
505 working directory now based on revision 0
548 working directory now based on revision 0
506 $ hg --cwd b parents --template 'parent: {rev}\n'
549 $ hg --cwd b parents --template 'parent: {rev}\n'
507 parent: 0
550 parent: 0
508
551
509 Test that "hg rollback" doesn't restore dirstate to one at the
552 Test that "hg rollback" doesn't restore dirstate to one at the
510 beginning of the rolled back transaction in not-"parent-gone" case.
553 beginning of the rolled back transaction in not-"parent-gone" case.
511
554
512 invoking pretxncommit hook will cause marking '.hg/dirstate' as a file
555 invoking pretxncommit hook will cause marking '.hg/dirstate' as a file
513 to be restored when rolling back, after DirstateTransactionPlan (see wiki
556 to be restored when rolling back, after DirstateTransactionPlan (see wiki
514 page for detail).
557 page for detail).
515
558
516 $ hg --cwd b branch -q foobar
559 $ hg --cwd b branch -q foobar
517 $ hg --cwd b commit -m foobar
560 $ hg --cwd b commit -m foobar
518 $ hg --cwd b update 0 -q
561 $ hg --cwd b update 0 -q
519 $ hg --cwd b import ../patch1 ../patch2 --config hooks.pretxncommit=true
562 $ hg --cwd b import ../patch1 ../patch2 --config hooks.pretxncommit=true
520 applying ../patch1
563 applying ../patch1
521 applying ../patch2
564 applying ../patch2
522 $ hg --cwd b update -q 1
565 $ hg --cwd b update -q 1
523 $ hg --cwd b rollback -q
566 $ hg --cwd b rollback -q
524 $ hg --cwd b parents --template 'parent: {rev}\n'
567 $ hg --cwd b parents --template 'parent: {rev}\n'
525 parent: 1
568 parent: 1
526
569
527 $ hg --cwd b update -q -C 0
570 $ hg --cwd b update -q -C 0
528 $ hg --cwd b --config extensions.strip= strip -q 1
571 $ hg --cwd b --config extensions.strip= strip -q 1
529
572
530 Test visibility of in-memory dirstate changes inside transaction to
573 Test visibility of in-memory dirstate changes inside transaction to
531 external process
574 external process
532
575
533 $ echo foo > a/foo
576 $ echo foo > a/foo
534 $ hg --cwd a commit -A -m 'adding foo' foo
577 $ hg --cwd a commit -A -m 'adding foo' foo
535 $ hg --cwd a export -o '../patch%R' 3
578 $ hg --cwd a export -o '../patch%R' 3
536
579
537 $ cat > $TESTTMP/checkvisibility.sh <<EOF
580 $ cat > $TESTTMP/checkvisibility.sh <<EOF
538 > echo "===="
581 > echo "===="
539 > hg parents --template "VISIBLE {rev}:{node|short}\n"
582 > hg parents --template "VISIBLE {rev}:{node|short}\n"
540 > hg status -amr
583 > hg status -amr
541 > # test that pending changes are hidden
584 > # test that pending changes are hidden
542 > unset HG_PENDING
585 > unset HG_PENDING
543 > hg parents --template "ACTUAL {rev}:{node|short}\n"
586 > hg parents --template "ACTUAL {rev}:{node|short}\n"
544 > hg status -amr
587 > hg status -amr
545 > echo "===="
588 > echo "===="
546 > EOF
589 > EOF
547
590
548 == test visibility to external editor
591 == test visibility to external editor
549
592
550 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
593 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
551 ====
594 ====
552 VISIBLE 0:80971e65b431
595 VISIBLE 0:80971e65b431
553 ACTUAL 0:80971e65b431
596 ACTUAL 0:80971e65b431
554 ====
597 ====
555
598
556 $ HGEDITOR="sh $TESTTMP/checkvisibility.sh" hg --cwd b import -v --edit ../patch1 ../patch2 ../patch3
599 $ HGEDITOR="sh $TESTTMP/checkvisibility.sh" hg --cwd b import -v --edit ../patch1 ../patch2 ../patch3
557 applying ../patch1
600 applying ../patch1
558 patching file a
601 patching file a
559 ====
602 ====
560 VISIBLE 0:80971e65b431
603 VISIBLE 0:80971e65b431
561 M a
604 M a
562 ACTUAL 0:80971e65b431
605 ACTUAL 0:80971e65b431
563 M a
606 M a
564 ====
607 ====
565 committing files:
608 committing files:
566 a
609 a
567 committing manifest
610 committing manifest
568 committing changelog
611 committing changelog
569 created 1d4bd90af0e4
612 created 1d4bd90af0e4
570 applying ../patch2
613 applying ../patch2
571 patching file a
614 patching file a
572 ====
615 ====
573 VISIBLE 1:1d4bd90af0e4
616 VISIBLE 1:1d4bd90af0e4
574 M a
617 M a
575 ACTUAL 0:80971e65b431
618 ACTUAL 0:80971e65b431
576 M a
619 M a
577 ====
620 ====
578 committing files:
621 committing files:
579 a
622 a
580 committing manifest
623 committing manifest
581 committing changelog
624 committing changelog
582 created 6d019af21222
625 created 6d019af21222
583 applying ../patch3
626 applying ../patch3
584 patching file foo
627 patching file foo
585 adding foo
628 adding foo
586 ====
629 ====
587 VISIBLE 2:6d019af21222
630 VISIBLE 2:6d019af21222
588 A foo
631 A foo
589 ACTUAL 0:80971e65b431
632 ACTUAL 0:80971e65b431
590 M a
633 M a
591 ====
634 ====
592 committing files:
635 committing files:
593 foo
636 foo
594 committing manifest
637 committing manifest
595 committing changelog
638 committing changelog
596 created 55e3f75b2378
639 created 55e3f75b2378
597
640
598 $ hg --cwd b rollback -q
641 $ hg --cwd b rollback -q
599
642
600 (content of file "a" is already changed and it should be recognized as
643 (content of file "a" is already changed and it should be recognized as
601 "M", even though dirstate is restored to one before "hg import")
644 "M", even though dirstate is restored to one before "hg import")
602
645
603 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
646 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
604 ====
647 ====
605 VISIBLE 0:80971e65b431
648 VISIBLE 0:80971e65b431
606 M a
649 M a
607 ACTUAL 0:80971e65b431
650 ACTUAL 0:80971e65b431
608 M a
651 M a
609 ====
652 ====
610 $ hg --cwd b revert --no-backup a
653 $ hg --cwd b revert --no-backup a
611 $ rm -f b/foo
654 $ rm -f b/foo
612
655
613 == test visibility to precommit external hook
656 == test visibility to precommit external hook
614
657
615 $ cat >> b/.hg/hgrc <<EOF
658 $ cat >> b/.hg/hgrc <<EOF
616 > [hooks]
659 > [hooks]
617 > precommit.visibility = sh $TESTTMP/checkvisibility.sh
660 > precommit.visibility = sh $TESTTMP/checkvisibility.sh
618 > EOF
661 > EOF
619
662
620 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
663 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
621 ====
664 ====
622 VISIBLE 0:80971e65b431
665 VISIBLE 0:80971e65b431
623 ACTUAL 0:80971e65b431
666 ACTUAL 0:80971e65b431
624 ====
667 ====
625
668
626 $ hg --cwd b import ../patch1 ../patch2 ../patch3
669 $ hg --cwd b import ../patch1 ../patch2 ../patch3
627 applying ../patch1
670 applying ../patch1
628 ====
671 ====
629 VISIBLE 0:80971e65b431
672 VISIBLE 0:80971e65b431
630 M a
673 M a
631 ACTUAL 0:80971e65b431
674 ACTUAL 0:80971e65b431
632 M a
675 M a
633 ====
676 ====
634 applying ../patch2
677 applying ../patch2
635 ====
678 ====
636 VISIBLE 1:1d4bd90af0e4
679 VISIBLE 1:1d4bd90af0e4
637 M a
680 M a
638 ACTUAL 0:80971e65b431
681 ACTUAL 0:80971e65b431
639 M a
682 M a
640 ====
683 ====
641 applying ../patch3
684 applying ../patch3
642 ====
685 ====
643 VISIBLE 2:6d019af21222
686 VISIBLE 2:6d019af21222
644 A foo
687 A foo
645 ACTUAL 0:80971e65b431
688 ACTUAL 0:80971e65b431
646 M a
689 M a
647 ====
690 ====
648
691
649 $ hg --cwd b rollback -q
692 $ hg --cwd b rollback -q
650 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
693 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
651 ====
694 ====
652 VISIBLE 0:80971e65b431
695 VISIBLE 0:80971e65b431
653 M a
696 M a
654 ACTUAL 0:80971e65b431
697 ACTUAL 0:80971e65b431
655 M a
698 M a
656 ====
699 ====
657 $ hg --cwd b revert --no-backup a
700 $ hg --cwd b revert --no-backup a
658 $ rm -f b/foo
701 $ rm -f b/foo
659
702
660 $ cat >> b/.hg/hgrc <<EOF
703 $ cat >> b/.hg/hgrc <<EOF
661 > [hooks]
704 > [hooks]
662 > precommit.visibility =
705 > precommit.visibility =
663 > EOF
706 > EOF
664
707
665 == test visibility to pretxncommit external hook
708 == test visibility to pretxncommit external hook
666
709
667 $ cat >> b/.hg/hgrc <<EOF
710 $ cat >> b/.hg/hgrc <<EOF
668 > [hooks]
711 > [hooks]
669 > pretxncommit.visibility = sh $TESTTMP/checkvisibility.sh
712 > pretxncommit.visibility = sh $TESTTMP/checkvisibility.sh
670 > EOF
713 > EOF
671
714
672 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
715 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
673 ====
716 ====
674 VISIBLE 0:80971e65b431
717 VISIBLE 0:80971e65b431
675 ACTUAL 0:80971e65b431
718 ACTUAL 0:80971e65b431
676 ====
719 ====
677
720
678 $ hg --cwd b import ../patch1 ../patch2 ../patch3
721 $ hg --cwd b import ../patch1 ../patch2 ../patch3
679 applying ../patch1
722 applying ../patch1
680 ====
723 ====
681 VISIBLE 0:80971e65b431
724 VISIBLE 0:80971e65b431
682 M a
725 M a
683 ACTUAL 0:80971e65b431
726 ACTUAL 0:80971e65b431
684 M a
727 M a
685 ====
728 ====
686 applying ../patch2
729 applying ../patch2
687 ====
730 ====
688 VISIBLE 1:1d4bd90af0e4
731 VISIBLE 1:1d4bd90af0e4
689 M a
732 M a
690 ACTUAL 0:80971e65b431
733 ACTUAL 0:80971e65b431
691 M a
734 M a
692 ====
735 ====
693 applying ../patch3
736 applying ../patch3
694 ====
737 ====
695 VISIBLE 2:6d019af21222
738 VISIBLE 2:6d019af21222
696 A foo
739 A foo
697 ACTUAL 0:80971e65b431
740 ACTUAL 0:80971e65b431
698 M a
741 M a
699 ====
742 ====
700
743
701 $ hg --cwd b rollback -q
744 $ hg --cwd b rollback -q
702 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
745 $ (cd b && sh "$TESTTMP/checkvisibility.sh")
703 ====
746 ====
704 VISIBLE 0:80971e65b431
747 VISIBLE 0:80971e65b431
705 M a
748 M a
706 ACTUAL 0:80971e65b431
749 ACTUAL 0:80971e65b431
707 M a
750 M a
708 ====
751 ====
709 $ hg --cwd b revert --no-backup a
752 $ hg --cwd b revert --no-backup a
710 $ rm -f b/foo
753 $ rm -f b/foo
711
754
712 $ cat >> b/.hg/hgrc <<EOF
755 $ cat >> b/.hg/hgrc <<EOF
713 > [hooks]
756 > [hooks]
714 > pretxncommit.visibility =
757 > pretxncommit.visibility =
715 > EOF
758 > EOF
716
759
717 $ rm -r b
760 $ rm -r b
718
761
719
762
720 importing a patch in a subdirectory failed at the commit stage
763 importing a patch in a subdirectory failed at the commit stage
721
764
722 $ echo line 2 >> a/d1/d2/a
765 $ echo line 2 >> a/d1/d2/a
723 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
766 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
724
767
725 hg import in a subdirectory
768 hg import in a subdirectory
726
769
727 $ hg clone -r0 a b
770 $ hg clone -r0 a b
728 adding changesets
771 adding changesets
729 adding manifests
772 adding manifests
730 adding file changes
773 adding file changes
731 added 1 changesets with 2 changes to 2 files
774 added 1 changesets with 2 changes to 2 files
732 new changesets 80971e65b431
775 new changesets 80971e65b431
733 updating to branch default
776 updating to branch default
734 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
777 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
735 $ hg --cwd a export tip > tmp
778 $ hg --cwd a export tip > tmp
736 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
779 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
737 $ dir=`pwd`
780 $ dir=`pwd`
738 $ cd b/d1/d2 2>&1 > /dev/null
781 $ cd b/d1/d2 2>&1 > /dev/null
739 $ hg import ../../../subdir-tip.patch
782 $ hg import ../../../subdir-tip.patch
740 applying ../../../subdir-tip.patch
783 applying ../../../subdir-tip.patch
741 $ cd "$dir"
784 $ cd "$dir"
742
785
743 message should be 'subdir change'
786 message should be 'subdir change'
744 committer should be 'someoneelse'
787 committer should be 'someoneelse'
745
788
746 $ hg --cwd b tip
789 $ hg --cwd b tip
747 changeset: 1:3577f5aea227
790 changeset: 1:3577f5aea227
748 tag: tip
791 tag: tip
749 user: someoneelse
792 user: someoneelse
750 date: Thu Jan 01 00:00:01 1970 +0000
793 date: Thu Jan 01 00:00:01 1970 +0000
751 summary: subdir change
794 summary: subdir change
752
795
753
796
754 should be empty
797 should be empty
755
798
756 $ hg --cwd b status
799 $ hg --cwd b status
757
800
758
801
759 Test fuzziness (ambiguous patch location, fuzz=2)
802 Test fuzziness (ambiguous patch location, fuzz=2)
760
803
761 $ hg init fuzzy
804 $ hg init fuzzy
762 $ cd fuzzy
805 $ cd fuzzy
763 $ echo line1 > a
806 $ echo line1 > a
764 $ echo line0 >> a
807 $ echo line0 >> a
765 $ echo line3 >> a
808 $ echo line3 >> a
766 $ hg ci -Am adda
809 $ hg ci -Am adda
767 adding a
810 adding a
768 $ echo line1 > a
811 $ echo line1 > a
769 $ echo line2 >> a
812 $ echo line2 >> a
770 $ echo line0 >> a
813 $ echo line0 >> a
771 $ echo line3 >> a
814 $ echo line3 >> a
772 $ hg ci -m change a
815 $ hg ci -m change a
773 $ hg export tip > fuzzy-tip.patch
816 $ hg export tip > fuzzy-tip.patch
774 $ hg up -C 0
817 $ hg up -C 0
775 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
818 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
776 $ echo line1 > a
819 $ echo line1 > a
777 $ echo line0 >> a
820 $ echo line0 >> a
778 $ echo line1 >> a
821 $ echo line1 >> a
779 $ echo line0 >> a
822 $ echo line0 >> a
780 $ hg ci -m brancha
823 $ hg ci -m brancha
781 created new head
824 created new head
782 $ hg import --config patch.fuzz=0 -v fuzzy-tip.patch
825 $ hg import --config patch.fuzz=0 -v fuzzy-tip.patch
783 applying fuzzy-tip.patch
826 applying fuzzy-tip.patch
784 patching file a
827 patching file a
785 Hunk #1 FAILED at 0
828 Hunk #1 FAILED at 0
786 1 out of 1 hunks FAILED -- saving rejects to file a.rej
829 1 out of 1 hunks FAILED -- saving rejects to file a.rej
787 abort: patch failed to apply
830 abort: patch failed to apply
788 [255]
831 [255]
789 $ hg import --no-commit -v fuzzy-tip.patch
832 $ hg import --no-commit -v fuzzy-tip.patch
790 applying fuzzy-tip.patch
833 applying fuzzy-tip.patch
791 patching file a
834 patching file a
792 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
835 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
793 applied to working directory
836 applied to working directory
794 $ hg revert -a
837 $ hg revert -a
795 reverting a
838 reverting a
796
839
797 Test --exact failure
840 Test --exact failure
798
841
799 $ sed 's/^# Parent .*/# Parent '"`hg log -r. -T '{node}'`"'/' \
842 $ sed 's/^# Parent .*/# Parent '"`hg log -r. -T '{node}'`"'/' \
800 > < fuzzy-tip.patch > fuzzy-reparent.patch
843 > < fuzzy-tip.patch > fuzzy-reparent.patch
801 $ hg import --config patch.fuzz=0 --exact fuzzy-reparent.patch
844 $ hg import --config patch.fuzz=0 --exact fuzzy-reparent.patch
802 applying fuzzy-reparent.patch
845 applying fuzzy-reparent.patch
803 patching file a
846 patching file a
804 Hunk #1 FAILED at 0
847 Hunk #1 FAILED at 0
805 1 out of 1 hunks FAILED -- saving rejects to file a.rej
848 1 out of 1 hunks FAILED -- saving rejects to file a.rej
806 abort: patch failed to apply
849 abort: patch failed to apply
807 [255]
850 [255]
808 $ hg up -qC
851 $ hg up -qC
809 $ hg import --config patch.fuzz=2 --exact fuzzy-reparent.patch
852 $ hg import --config patch.fuzz=2 --exact fuzzy-reparent.patch
810 applying fuzzy-reparent.patch
853 applying fuzzy-reparent.patch
811 patching file a
854 patching file a
812 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
855 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
813 transaction abort!
856 transaction abort!
814 rollback completed
857 rollback completed
815 abort: patch is damaged or loses information
858 abort: patch is damaged or loses information
816 [255]
859 [255]
817 $ hg up -qC
860 $ hg up -qC
818
861
819 $ grep '^#' fuzzy-tip.patch > empty.patch
862 $ grep '^#' fuzzy-tip.patch > empty.patch
820 $ cat <<'EOF' >> empty.patch
863 $ cat <<'EOF' >> empty.patch
821 > change
864 > change
822 >
865 >
823 > diff -r bb90ef1daa38 -r 0e9b883378d4 a
866 > diff -r bb90ef1daa38 -r 0e9b883378d4 a
824 > --- a/a Thu Jan 01 00:00:00 1970 +0000
867 > --- a/a Thu Jan 01 00:00:00 1970 +0000
825 > --- b/a Thu Jan 01 00:00:00 1970 +0000
868 > --- b/a Thu Jan 01 00:00:00 1970 +0000
826 > EOF
869 > EOF
827 $ hg import --exact empty.patch
870 $ hg import --exact empty.patch
828 applying empty.patch
871 applying empty.patch
829 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
872 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 abort: patch is damaged or loses information
873 abort: patch is damaged or loses information
831 [255]
874 [255]
832 $ hg up -qC
875 $ hg up -qC
833
876
834 import with --no-commit should have written .hg/last-message.txt
877 import with --no-commit should have written .hg/last-message.txt
835
878
836 $ cat .hg/last-message.txt
879 $ cat .hg/last-message.txt
837 change (no-eol)
880 change (no-eol)
838
881
839
882
840 test fuzziness with eol=auto
883 test fuzziness with eol=auto
841
884
842 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
885 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
843 applying fuzzy-tip.patch
886 applying fuzzy-tip.patch
844 patching file a
887 patching file a
845 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
888 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
846 applied to working directory
889 applied to working directory
847 $ cd ..
890 $ cd ..
848
891
849
892
850 Test hunk touching empty files (issue906)
893 Test hunk touching empty files (issue906)
851
894
852 $ hg init empty
895 $ hg init empty
853 $ cd empty
896 $ cd empty
854 $ touch a
897 $ touch a
855 $ touch b1
898 $ touch b1
856 $ touch c1
899 $ touch c1
857 $ echo d > d
900 $ echo d > d
858 $ hg ci -Am init
901 $ hg ci -Am init
859 adding a
902 adding a
860 adding b1
903 adding b1
861 adding c1
904 adding c1
862 adding d
905 adding d
863 $ echo a > a
906 $ echo a > a
864 $ echo b > b1
907 $ echo b > b1
865 $ hg mv b1 b2
908 $ hg mv b1 b2
866 $ echo c > c1
909 $ echo c > c1
867 $ hg copy c1 c2
910 $ hg copy c1 c2
868 $ rm d
911 $ rm d
869 $ touch d
912 $ touch d
870 $ hg diff --git
913 $ hg diff --git
871 diff --git a/a b/a
914 diff --git a/a b/a
872 --- a/a
915 --- a/a
873 +++ b/a
916 +++ b/a
874 @@ -0,0 +1,1 @@
917 @@ -0,0 +1,1 @@
875 +a
918 +a
876 diff --git a/b1 b/b2
919 diff --git a/b1 b/b2
877 rename from b1
920 rename from b1
878 rename to b2
921 rename to b2
879 --- a/b1
922 --- a/b1
880 +++ b/b2
923 +++ b/b2
881 @@ -0,0 +1,1 @@
924 @@ -0,0 +1,1 @@
882 +b
925 +b
883 diff --git a/c1 b/c1
926 diff --git a/c1 b/c1
884 --- a/c1
927 --- a/c1
885 +++ b/c1
928 +++ b/c1
886 @@ -0,0 +1,1 @@
929 @@ -0,0 +1,1 @@
887 +c
930 +c
888 diff --git a/c1 b/c2
931 diff --git a/c1 b/c2
889 copy from c1
932 copy from c1
890 copy to c2
933 copy to c2
891 --- a/c1
934 --- a/c1
892 +++ b/c2
935 +++ b/c2
893 @@ -0,0 +1,1 @@
936 @@ -0,0 +1,1 @@
894 +c
937 +c
895 diff --git a/d b/d
938 diff --git a/d b/d
896 --- a/d
939 --- a/d
897 +++ b/d
940 +++ b/d
898 @@ -1,1 +0,0 @@
941 @@ -1,1 +0,0 @@
899 -d
942 -d
900 $ hg ci -m empty
943 $ hg ci -m empty
901 $ hg export --git tip > empty.diff
944 $ hg export --git tip > empty.diff
902 $ hg up -C 0
945 $ hg up -C 0
903 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
946 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
904 $ hg import empty.diff
947 $ hg import empty.diff
905 applying empty.diff
948 applying empty.diff
906 $ for name in a b1 b2 c1 c2 d; do
949 $ for name in a b1 b2 c1 c2 d; do
907 > echo % $name file
950 > echo % $name file
908 > test -f $name && cat $name
951 > test -f $name && cat $name
909 > done
952 > done
910 % a file
953 % a file
911 a
954 a
912 % b1 file
955 % b1 file
913 % b2 file
956 % b2 file
914 b
957 b
915 % c1 file
958 % c1 file
916 c
959 c
917 % c2 file
960 % c2 file
918 c
961 c
919 % d file
962 % d file
920 $ cd ..
963 $ cd ..
921
964
922
965
923 Test importing a patch ending with a binary file removal
966 Test importing a patch ending with a binary file removal
924
967
925 $ hg init binaryremoval
968 $ hg init binaryremoval
926 $ cd binaryremoval
969 $ cd binaryremoval
927 $ echo a > a
970 $ echo a > a
928 $ "$PYTHON" -c "open('b', 'wb').write(b'a\x00b')"
971 $ "$PYTHON" -c "open('b', 'wb').write(b'a\x00b')"
929 $ hg ci -Am addall
972 $ hg ci -Am addall
930 adding a
973 adding a
931 adding b
974 adding b
932 $ hg rm a
975 $ hg rm a
933 $ hg rm b
976 $ hg rm b
934 $ hg st
977 $ hg st
935 R a
978 R a
936 R b
979 R b
937 $ hg ci -m remove
980 $ hg ci -m remove
938 $ hg export --git . > remove.diff
981 $ hg export --git . > remove.diff
939 $ cat remove.diff | grep git
982 $ cat remove.diff | grep git
940 diff --git a/a b/a
983 diff --git a/a b/a
941 diff --git a/b b/b
984 diff --git a/b b/b
942 $ hg up -C 0
985 $ hg up -C 0
943 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
986 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
944 $ hg import remove.diff
987 $ hg import remove.diff
945 applying remove.diff
988 applying remove.diff
946 $ hg manifest
989 $ hg manifest
947 $ cd ..
990 $ cd ..
948
991
949
992
950 Issue927: test update+rename with common name
993 Issue927: test update+rename with common name
951
994
952 $ hg init t
995 $ hg init t
953 $ cd t
996 $ cd t
954 $ touch a
997 $ touch a
955 $ hg ci -Am t
998 $ hg ci -Am t
956 adding a
999 adding a
957 $ echo a > a
1000 $ echo a > a
958
1001
959 Here, bfile.startswith(afile)
1002 Here, bfile.startswith(afile)
960
1003
961 $ hg copy a a2
1004 $ hg copy a a2
962 $ hg ci -m copya
1005 $ hg ci -m copya
963 $ hg export --git tip > copy.diff
1006 $ hg export --git tip > copy.diff
964 $ hg up -C 0
1007 $ hg up -C 0
965 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1008 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
966 $ hg import copy.diff
1009 $ hg import copy.diff
967 applying copy.diff
1010 applying copy.diff
968
1011
969 a should contain an 'a'
1012 a should contain an 'a'
970
1013
971 $ cat a
1014 $ cat a
972 a
1015 a
973
1016
974 and a2 should have duplicated it
1017 and a2 should have duplicated it
975
1018
976 $ cat a2
1019 $ cat a2
977 a
1020 a
978 $ cd ..
1021 $ cd ..
979
1022
980
1023
981 test -p0
1024 test -p0
982
1025
983 $ hg init p0
1026 $ hg init p0
984 $ cd p0
1027 $ cd p0
985 $ echo a > a
1028 $ echo a > a
986 $ hg ci -Am t
1029 $ hg ci -Am t
987 adding a
1030 adding a
988 $ hg import -p foo
1031 $ hg import -p foo
989 abort: invalid value 'foo' for option -p, expected int
1032 abort: invalid value 'foo' for option -p, expected int
990 [255]
1033 [255]
991 $ hg import -p0 - << EOF
1034 $ hg import -p0 - << EOF
992 > foobar
1035 > foobar
993 > --- a Sat Apr 12 22:43:58 2008 -0400
1036 > --- a Sat Apr 12 22:43:58 2008 -0400
994 > +++ a Sat Apr 12 22:44:05 2008 -0400
1037 > +++ a Sat Apr 12 22:44:05 2008 -0400
995 > @@ -1,1 +1,1 @@
1038 > @@ -1,1 +1,1 @@
996 > -a
1039 > -a
997 > +bb
1040 > +bb
998 > EOF
1041 > EOF
999 applying patch from stdin
1042 applying patch from stdin
1000 $ hg status
1043 $ hg status
1001 $ cat a
1044 $ cat a
1002 bb
1045 bb
1003
1046
1004 test --prefix
1047 test --prefix
1005
1048
1006 $ mkdir -p dir/dir2
1049 $ mkdir -p dir/dir2
1007 $ echo b > dir/dir2/b
1050 $ echo b > dir/dir2/b
1008 $ hg ci -Am b
1051 $ hg ci -Am b
1009 adding dir/dir2/b
1052 adding dir/dir2/b
1010 $ hg import -p2 --prefix dir - << EOF
1053 $ hg import -p2 --prefix dir - << EOF
1011 > foobar
1054 > foobar
1012 > --- drop1/drop2/dir2/b
1055 > --- drop1/drop2/dir2/b
1013 > +++ drop1/drop2/dir2/b
1056 > +++ drop1/drop2/dir2/b
1014 > @@ -1,1 +1,1 @@
1057 > @@ -1,1 +1,1 @@
1015 > -b
1058 > -b
1016 > +cc
1059 > +cc
1017 > EOF
1060 > EOF
1018 applying patch from stdin
1061 applying patch from stdin
1019 $ hg status
1062 $ hg status
1020 $ cat dir/dir2/b
1063 $ cat dir/dir2/b
1021 cc
1064 cc
1022 $ cd ..
1065 $ cd ..
1023
1066
1024
1067
1025 test paths outside repo root
1068 test paths outside repo root
1026
1069
1027 $ mkdir outside
1070 $ mkdir outside
1028 $ touch outside/foo
1071 $ touch outside/foo
1029 $ hg init inside
1072 $ hg init inside
1030 $ cd inside
1073 $ cd inside
1031 $ hg import - <<EOF
1074 $ hg import - <<EOF
1032 > diff --git a/a b/b
1075 > diff --git a/a b/b
1033 > rename from ../outside/foo
1076 > rename from ../outside/foo
1034 > rename to bar
1077 > rename to bar
1035 > EOF
1078 > EOF
1036 applying patch from stdin
1079 applying patch from stdin
1037 abort: path contains illegal component: ../outside/foo
1080 abort: path contains illegal component: ../outside/foo
1038 [255]
1081 [255]
1039 $ cd ..
1082 $ cd ..
1040
1083
1041
1084
1042 test import with similarity and git and strip (issue295 et al.)
1085 test import with similarity and git and strip (issue295 et al.)
1043
1086
1044 $ hg init sim
1087 $ hg init sim
1045 $ cd sim
1088 $ cd sim
1046 $ echo 'this is a test' > a
1089 $ echo 'this is a test' > a
1047 $ hg ci -Ama
1090 $ hg ci -Ama
1048 adding a
1091 adding a
1049 $ cat > ../rename.diff <<EOF
1092 $ cat > ../rename.diff <<EOF
1050 > diff --git a/foo/a b/foo/a
1093 > diff --git a/foo/a b/foo/a
1051 > deleted file mode 100644
1094 > deleted file mode 100644
1052 > --- a/foo/a
1095 > --- a/foo/a
1053 > +++ /dev/null
1096 > +++ /dev/null
1054 > @@ -1,1 +0,0 @@
1097 > @@ -1,1 +0,0 @@
1055 > -this is a test
1098 > -this is a test
1056 > diff --git a/foo/b b/foo/b
1099 > diff --git a/foo/b b/foo/b
1057 > new file mode 100644
1100 > new file mode 100644
1058 > --- /dev/null
1101 > --- /dev/null
1059 > +++ b/foo/b
1102 > +++ b/foo/b
1060 > @@ -0,0 +1,2 @@
1103 > @@ -0,0 +1,2 @@
1061 > +this is a test
1104 > +this is a test
1062 > +foo
1105 > +foo
1063 > EOF
1106 > EOF
1064 $ hg import --no-commit -v -s 1 ../rename.diff -p2
1107 $ hg import --no-commit -v -s 1 ../rename.diff -p2
1065 applying ../rename.diff
1108 applying ../rename.diff
1066 patching file a
1109 patching file a
1067 patching file b
1110 patching file b
1068 adding b
1111 adding b
1069 recording removal of a as rename to b (88% similar)
1112 recording removal of a as rename to b (88% similar)
1070 applied to working directory
1113 applied to working directory
1071 $ echo 'mod b' > b
1114 $ echo 'mod b' > b
1072 $ hg st -C
1115 $ hg st -C
1073 A b
1116 A b
1074 a
1117 a
1075 R a
1118 R a
1076 $ hg revert -a
1119 $ hg revert -a
1077 forgetting b
1120 forgetting b
1078 undeleting a
1121 undeleting a
1079 $ cat b
1122 $ cat b
1080 mod b
1123 mod b
1081 $ rm b
1124 $ rm b
1082 $ hg import --no-commit -v -s 100 ../rename.diff -p2
1125 $ hg import --no-commit -v -s 100 ../rename.diff -p2
1083 applying ../rename.diff
1126 applying ../rename.diff
1084 patching file a
1127 patching file a
1085 patching file b
1128 patching file b
1086 adding b
1129 adding b
1087 applied to working directory
1130 applied to working directory
1088 $ hg st -C
1131 $ hg st -C
1089 A b
1132 A b
1090 R a
1133 R a
1091 $ cd ..
1134 $ cd ..
1092
1135
1093
1136
1094 Issue1495: add empty file from the end of patch
1137 Issue1495: add empty file from the end of patch
1095
1138
1096 $ hg init addemptyend
1139 $ hg init addemptyend
1097 $ cd addemptyend
1140 $ cd addemptyend
1098 $ touch a
1141 $ touch a
1099 $ hg addremove
1142 $ hg addremove
1100 adding a
1143 adding a
1101 $ hg ci -m "commit"
1144 $ hg ci -m "commit"
1102 $ cat > a.patch <<EOF
1145 $ cat > a.patch <<EOF
1103 > add a, b
1146 > add a, b
1104 > diff --git a/a b/a
1147 > diff --git a/a b/a
1105 > --- a/a
1148 > --- a/a
1106 > +++ b/a
1149 > +++ b/a
1107 > @@ -0,0 +1,1 @@
1150 > @@ -0,0 +1,1 @@
1108 > +a
1151 > +a
1109 > diff --git a/b b/b
1152 > diff --git a/b b/b
1110 > new file mode 100644
1153 > new file mode 100644
1111 > EOF
1154 > EOF
1112 $ hg import --no-commit a.patch
1155 $ hg import --no-commit a.patch
1113 applying a.patch
1156 applying a.patch
1114
1157
1115 apply a good patch followed by an empty patch (mainly to ensure
1158 apply a good patch followed by an empty patch (mainly to ensure
1116 that dirstate is *not* updated when import crashes)
1159 that dirstate is *not* updated when import crashes)
1117 $ hg update -q -C .
1160 $ hg update -q -C .
1118 $ rm b
1161 $ rm b
1119 $ touch empty.patch
1162 $ touch empty.patch
1120 $ hg import a.patch empty.patch
1163 $ hg import a.patch empty.patch
1121 applying a.patch
1164 applying a.patch
1122 applying empty.patch
1165 applying empty.patch
1123 transaction abort!
1166 transaction abort!
1124 rollback completed
1167 rollback completed
1125 abort: empty.patch: no diffs found
1168 abort: empty.patch: no diffs found
1126 [255]
1169 [255]
1127 $ hg tip --template '{rev} {desc|firstline}\n'
1170 $ hg tip --template '{rev} {desc|firstline}\n'
1128 0 commit
1171 0 commit
1129 $ hg -q status
1172 $ hg -q status
1130 M a
1173 M a
1131 $ cd ..
1174 $ cd ..
1132
1175
1133 create file when source is not /dev/null
1176 create file when source is not /dev/null
1134
1177
1135 $ cat > create.patch <<EOF
1178 $ cat > create.patch <<EOF
1136 > diff -Naur proj-orig/foo proj-new/foo
1179 > diff -Naur proj-orig/foo proj-new/foo
1137 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
1180 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
1138 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
1181 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
1139 > @@ -0,0 +1,1 @@
1182 > @@ -0,0 +1,1 @@
1140 > +a
1183 > +a
1141 > EOF
1184 > EOF
1142
1185
1143 some people have patches like the following too
1186 some people have patches like the following too
1144
1187
1145 $ cat > create2.patch <<EOF
1188 $ cat > create2.patch <<EOF
1146 > diff -Naur proj-orig/foo proj-new/foo
1189 > diff -Naur proj-orig/foo proj-new/foo
1147 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
1190 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
1148 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
1191 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
1149 > @@ -0,0 +1,1 @@
1192 > @@ -0,0 +1,1 @@
1150 > +a
1193 > +a
1151 > EOF
1194 > EOF
1152 $ hg init oddcreate
1195 $ hg init oddcreate
1153 $ cd oddcreate
1196 $ cd oddcreate
1154 $ hg import --no-commit ../create.patch
1197 $ hg import --no-commit ../create.patch
1155 applying ../create.patch
1198 applying ../create.patch
1156 $ cat foo
1199 $ cat foo
1157 a
1200 a
1158 $ rm foo
1201 $ rm foo
1159 $ hg revert foo
1202 $ hg revert foo
1160 $ hg import --no-commit ../create2.patch
1203 $ hg import --no-commit ../create2.patch
1161 applying ../create2.patch
1204 applying ../create2.patch
1162 $ cat foo
1205 $ cat foo
1163 a
1206 a
1164
1207
1165 $ cd ..
1208 $ cd ..
1166
1209
1167 Issue1859: first line mistaken for email headers
1210 Issue1859: first line mistaken for email headers
1168
1211
1169 $ hg init emailconfusion
1212 $ hg init emailconfusion
1170 $ cd emailconfusion
1213 $ cd emailconfusion
1171 $ cat > a.patch <<EOF
1214 $ cat > a.patch <<EOF
1172 > module: summary
1215 > module: summary
1173 >
1216 >
1174 > description
1217 > description
1175 >
1218 >
1176 >
1219 >
1177 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
1220 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
1178 > --- /dev/null
1221 > --- /dev/null
1179 > +++ b/a
1222 > +++ b/a
1180 > @@ -0,0 +1,1 @@
1223 > @@ -0,0 +1,1 @@
1181 > +a
1224 > +a
1182 > EOF
1225 > EOF
1183 $ hg import -d '0 0' a.patch
1226 $ hg import -d '0 0' a.patch
1184 applying a.patch
1227 applying a.patch
1185 $ hg parents -v
1228 $ hg parents -v
1186 changeset: 0:5a681217c0ad
1229 changeset: 0:5a681217c0ad
1187 tag: tip
1230 tag: tip
1188 user: test
1231 user: test
1189 date: Thu Jan 01 00:00:00 1970 +0000
1232 date: Thu Jan 01 00:00:00 1970 +0000
1190 files: a
1233 files: a
1191 description:
1234 description:
1192 module: summary
1235 module: summary
1193
1236
1194 description
1237 description
1195
1238
1196
1239
1197 $ cd ..
1240 $ cd ..
1198
1241
1199
1242
1200 in commit message
1243 in commit message
1201
1244
1202 $ hg init commitconfusion
1245 $ hg init commitconfusion
1203 $ cd commitconfusion
1246 $ cd commitconfusion
1204 $ cat > a.patch <<EOF
1247 $ cat > a.patch <<EOF
1205 > module: summary
1248 > module: summary
1206 >
1249 >
1207 > --- description
1250 > --- description
1208 >
1251 >
1209 > diff --git a/a b/a
1252 > diff --git a/a b/a
1210 > new file mode 100644
1253 > new file mode 100644
1211 > --- /dev/null
1254 > --- /dev/null
1212 > +++ b/a
1255 > +++ b/a
1213 > @@ -0,0 +1,1 @@
1256 > @@ -0,0 +1,1 @@
1214 > +a
1257 > +a
1215 > EOF
1258 > EOF
1216 > hg import -d '0 0' a.patch
1259 > hg import -d '0 0' a.patch
1217 > hg parents -v
1260 > hg parents -v
1218 > cd ..
1261 > cd ..
1219 >
1262 >
1220 > echo '% tricky header splitting'
1263 > echo '% tricky header splitting'
1221 > cat > trickyheaders.patch <<EOF
1264 > cat > trickyheaders.patch <<EOF
1222 > From: User A <user@a>
1265 > From: User A <user@a>
1223 > Subject: [PATCH] from: tricky!
1266 > Subject: [PATCH] from: tricky!
1224 >
1267 >
1225 > # HG changeset patch
1268 > # HG changeset patch
1226 > # User User B
1269 > # User User B
1227 > # Date 1266264441 18000
1270 > # Date 1266264441 18000
1228 > # Branch stable
1271 > # Branch stable
1229 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
1272 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
1230 > # Parent 0000000000000000000000000000000000000000
1273 > # Parent 0000000000000000000000000000000000000000
1231 > from: tricky!
1274 > from: tricky!
1232 >
1275 >
1233 > That is not a header.
1276 > That is not a header.
1234 >
1277 >
1235 > diff -r 000000000000 -r f2be6a1170ac foo
1278 > diff -r 000000000000 -r f2be6a1170ac foo
1236 > --- /dev/null
1279 > --- /dev/null
1237 > +++ b/foo
1280 > +++ b/foo
1238 > @@ -0,0 +1,1 @@
1281 > @@ -0,0 +1,1 @@
1239 > +foo
1282 > +foo
1240 > EOF
1283 > EOF
1241 applying a.patch
1284 applying a.patch
1242 changeset: 0:f34d9187897d
1285 changeset: 0:f34d9187897d
1243 tag: tip
1286 tag: tip
1244 user: test
1287 user: test
1245 date: Thu Jan 01 00:00:00 1970 +0000
1288 date: Thu Jan 01 00:00:00 1970 +0000
1246 files: a
1289 files: a
1247 description:
1290 description:
1248 module: summary
1291 module: summary
1249
1292
1250
1293
1251 % tricky header splitting
1294 % tricky header splitting
1252
1295
1253 $ hg init trickyheaders
1296 $ hg init trickyheaders
1254 $ cd trickyheaders
1297 $ cd trickyheaders
1255 $ hg import -d '0 0' ../trickyheaders.patch
1298 $ hg import -d '0 0' ../trickyheaders.patch
1256 applying ../trickyheaders.patch
1299 applying ../trickyheaders.patch
1257 $ hg export --git tip
1300 $ hg export --git tip
1258 # HG changeset patch
1301 # HG changeset patch
1259 # User User B
1302 # User User B
1260 # Date 0 0
1303 # Date 0 0
1261 # Thu Jan 01 00:00:00 1970 +0000
1304 # Thu Jan 01 00:00:00 1970 +0000
1262 # Node ID eb56ab91903632294ac504838508cb370c0901d2
1305 # Node ID eb56ab91903632294ac504838508cb370c0901d2
1263 # Parent 0000000000000000000000000000000000000000
1306 # Parent 0000000000000000000000000000000000000000
1264 from: tricky!
1307 from: tricky!
1265
1308
1266 That is not a header.
1309 That is not a header.
1267
1310
1268 diff --git a/foo b/foo
1311 diff --git a/foo b/foo
1269 new file mode 100644
1312 new file mode 100644
1270 --- /dev/null
1313 --- /dev/null
1271 +++ b/foo
1314 +++ b/foo
1272 @@ -0,0 +1,1 @@
1315 @@ -0,0 +1,1 @@
1273 +foo
1316 +foo
1274 $ cd ..
1317 $ cd ..
1275
1318
1276
1319
1277 Issue2102: hg export and hg import speak different languages
1320 Issue2102: hg export and hg import speak different languages
1278
1321
1279 $ hg init issue2102
1322 $ hg init issue2102
1280 $ cd issue2102
1323 $ cd issue2102
1281 $ mkdir -p src/cmd/gc
1324 $ mkdir -p src/cmd/gc
1282 $ touch src/cmd/gc/mksys.bash
1325 $ touch src/cmd/gc/mksys.bash
1283 $ hg ci -Am init
1326 $ hg ci -Am init
1284 adding src/cmd/gc/mksys.bash
1327 adding src/cmd/gc/mksys.bash
1285 $ hg import - <<EOF
1328 $ hg import - <<EOF
1286 > # HG changeset patch
1329 > # HG changeset patch
1287 > # User Rob Pike
1330 > # User Rob Pike
1288 > # Date 1216685449 25200
1331 > # Date 1216685449 25200
1289 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
1332 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
1290 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
1333 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
1291 > help management of empty pkg and lib directories in perforce
1334 > help management of empty pkg and lib directories in perforce
1292 >
1335 >
1293 > R=gri
1336 > R=gri
1294 > DELTA=4 (4 added, 0 deleted, 0 changed)
1337 > DELTA=4 (4 added, 0 deleted, 0 changed)
1295 > OCL=13328
1338 > OCL=13328
1296 > CL=13328
1339 > CL=13328
1297 >
1340 >
1298 > diff --git a/lib/place-holder b/lib/place-holder
1341 > diff --git a/lib/place-holder b/lib/place-holder
1299 > new file mode 100644
1342 > new file mode 100644
1300 > --- /dev/null
1343 > --- /dev/null
1301 > +++ b/lib/place-holder
1344 > +++ b/lib/place-holder
1302 > @@ -0,0 +1,2 @@
1345 > @@ -0,0 +1,2 @@
1303 > +perforce does not maintain empty directories.
1346 > +perforce does not maintain empty directories.
1304 > +this file helps.
1347 > +this file helps.
1305 > diff --git a/pkg/place-holder b/pkg/place-holder
1348 > diff --git a/pkg/place-holder b/pkg/place-holder
1306 > new file mode 100644
1349 > new file mode 100644
1307 > --- /dev/null
1350 > --- /dev/null
1308 > +++ b/pkg/place-holder
1351 > +++ b/pkg/place-holder
1309 > @@ -0,0 +1,2 @@
1352 > @@ -0,0 +1,2 @@
1310 > +perforce does not maintain empty directories.
1353 > +perforce does not maintain empty directories.
1311 > +this file helps.
1354 > +this file helps.
1312 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1355 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1313 > old mode 100644
1356 > old mode 100644
1314 > new mode 100755
1357 > new mode 100755
1315 > EOF
1358 > EOF
1316 applying patch from stdin
1359 applying patch from stdin
1317
1360
1318 #if execbit
1361 #if execbit
1319
1362
1320 $ hg sum
1363 $ hg sum
1321 parent: 1:d59915696727 tip
1364 parent: 1:d59915696727 tip
1322 help management of empty pkg and lib directories in perforce
1365 help management of empty pkg and lib directories in perforce
1323 branch: default
1366 branch: default
1324 commit: (clean)
1367 commit: (clean)
1325 update: (current)
1368 update: (current)
1326 phases: 2 draft
1369 phases: 2 draft
1327
1370
1328 $ hg diff --git -c tip
1371 $ hg diff --git -c tip
1329 diff --git a/lib/place-holder b/lib/place-holder
1372 diff --git a/lib/place-holder b/lib/place-holder
1330 new file mode 100644
1373 new file mode 100644
1331 --- /dev/null
1374 --- /dev/null
1332 +++ b/lib/place-holder
1375 +++ b/lib/place-holder
1333 @@ -0,0 +1,2 @@
1376 @@ -0,0 +1,2 @@
1334 +perforce does not maintain empty directories.
1377 +perforce does not maintain empty directories.
1335 +this file helps.
1378 +this file helps.
1336 diff --git a/pkg/place-holder b/pkg/place-holder
1379 diff --git a/pkg/place-holder b/pkg/place-holder
1337 new file mode 100644
1380 new file mode 100644
1338 --- /dev/null
1381 --- /dev/null
1339 +++ b/pkg/place-holder
1382 +++ b/pkg/place-holder
1340 @@ -0,0 +1,2 @@
1383 @@ -0,0 +1,2 @@
1341 +perforce does not maintain empty directories.
1384 +perforce does not maintain empty directories.
1342 +this file helps.
1385 +this file helps.
1343 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1386 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1344 old mode 100644
1387 old mode 100644
1345 new mode 100755
1388 new mode 100755
1346
1389
1347 #else
1390 #else
1348
1391
1349 $ hg sum
1392 $ hg sum
1350 parent: 1:28f089cc9ccc tip
1393 parent: 1:28f089cc9ccc tip
1351 help management of empty pkg and lib directories in perforce
1394 help management of empty pkg and lib directories in perforce
1352 branch: default
1395 branch: default
1353 commit: (clean)
1396 commit: (clean)
1354 update: (current)
1397 update: (current)
1355 phases: 2 draft
1398 phases: 2 draft
1356
1399
1357 $ hg diff --git -c tip
1400 $ hg diff --git -c tip
1358 diff --git a/lib/place-holder b/lib/place-holder
1401 diff --git a/lib/place-holder b/lib/place-holder
1359 new file mode 100644
1402 new file mode 100644
1360 --- /dev/null
1403 --- /dev/null
1361 +++ b/lib/place-holder
1404 +++ b/lib/place-holder
1362 @@ -0,0 +1,2 @@
1405 @@ -0,0 +1,2 @@
1363 +perforce does not maintain empty directories.
1406 +perforce does not maintain empty directories.
1364 +this file helps.
1407 +this file helps.
1365 diff --git a/pkg/place-holder b/pkg/place-holder
1408 diff --git a/pkg/place-holder b/pkg/place-holder
1366 new file mode 100644
1409 new file mode 100644
1367 --- /dev/null
1410 --- /dev/null
1368 +++ b/pkg/place-holder
1411 +++ b/pkg/place-holder
1369 @@ -0,0 +1,2 @@
1412 @@ -0,0 +1,2 @@
1370 +perforce does not maintain empty directories.
1413 +perforce does not maintain empty directories.
1371 +this file helps.
1414 +this file helps.
1372
1415
1373 /* The mode change for mksys.bash is missing here, because on platforms */
1416 /* The mode change for mksys.bash is missing here, because on platforms */
1374 /* that don't support execbits, mode changes in patches are ignored when */
1417 /* that don't support execbits, mode changes in patches are ignored when */
1375 /* they are imported. This is obviously also the reason for why the hash */
1418 /* they are imported. This is obviously also the reason for why the hash */
1376 /* in the created changeset is different to the one you see above the */
1419 /* in the created changeset is different to the one you see above the */
1377 /* #else clause */
1420 /* #else clause */
1378
1421
1379 #endif
1422 #endif
1380 $ cd ..
1423 $ cd ..
1381
1424
1382
1425
1383 diff lines looking like headers
1426 diff lines looking like headers
1384
1427
1385 $ hg init difflineslikeheaders
1428 $ hg init difflineslikeheaders
1386 $ cd difflineslikeheaders
1429 $ cd difflineslikeheaders
1387 $ echo a >a
1430 $ echo a >a
1388 $ echo b >b
1431 $ echo b >b
1389 $ echo c >c
1432 $ echo c >c
1390 $ hg ci -Am1
1433 $ hg ci -Am1
1391 adding a
1434 adding a
1392 adding b
1435 adding b
1393 adding c
1436 adding c
1394
1437
1395 $ echo "key: value" >>a
1438 $ echo "key: value" >>a
1396 $ echo "key: value" >>b
1439 $ echo "key: value" >>b
1397 $ echo "foo" >>c
1440 $ echo "foo" >>c
1398 $ hg ci -m2
1441 $ hg ci -m2
1399
1442
1400 $ hg up -C 0
1443 $ hg up -C 0
1401 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1444 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1402 $ hg diff --git -c1 >want
1445 $ hg diff --git -c1 >want
1403 $ hg diff -c1 | hg import --no-commit -
1446 $ hg diff -c1 | hg import --no-commit -
1404 applying patch from stdin
1447 applying patch from stdin
1405 $ hg diff --git >have
1448 $ hg diff --git >have
1406 $ diff want have
1449 $ diff want have
1407 $ cd ..
1450 $ cd ..
1408
1451
1409 import a unified diff with no lines of context (diff -U0)
1452 import a unified diff with no lines of context (diff -U0)
1410
1453
1411 $ hg init diffzero
1454 $ hg init diffzero
1412 $ cd diffzero
1455 $ cd diffzero
1413 $ cat > f << EOF
1456 $ cat > f << EOF
1414 > c2
1457 > c2
1415 > c4
1458 > c4
1416 > c5
1459 > c5
1417 > EOF
1460 > EOF
1418 $ hg commit -Am0
1461 $ hg commit -Am0
1419 adding f
1462 adding f
1420
1463
1421 $ hg import --no-commit - << EOF
1464 $ hg import --no-commit - << EOF
1422 > # HG changeset patch
1465 > # HG changeset patch
1423 > # User test
1466 > # User test
1424 > # Date 0 0
1467 > # Date 0 0
1425 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1468 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1426 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1469 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1427 > 1
1470 > 1
1428 > diff -r 8679a12a975b -r f4974ab632f3 f
1471 > diff -r 8679a12a975b -r f4974ab632f3 f
1429 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1472 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1430 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1473 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1431 > @@ -0,0 +1,1 @@
1474 > @@ -0,0 +1,1 @@
1432 > +c1
1475 > +c1
1433 > @@ -1,0 +3,1 @@
1476 > @@ -1,0 +3,1 @@
1434 > +c3
1477 > +c3
1435 > @@ -3,1 +4,0 @@
1478 > @@ -3,1 +4,0 @@
1436 > -c5
1479 > -c5
1437 > EOF
1480 > EOF
1438 applying patch from stdin
1481 applying patch from stdin
1439
1482
1440 $ cat f
1483 $ cat f
1441 c1
1484 c1
1442 c2
1485 c2
1443 c3
1486 c3
1444 c4
1487 c4
1445
1488
1446 $ cd ..
1489 $ cd ..
1447
1490
1448 commit message that looks like a diff header (issue1879)
1491 commit message that looks like a diff header (issue1879)
1449
1492
1450 $ hg init headerlikemsg
1493 $ hg init headerlikemsg
1451 $ cd headerlikemsg
1494 $ cd headerlikemsg
1452 $ touch empty
1495 $ touch empty
1453 $ echo nonempty >> nonempty
1496 $ echo nonempty >> nonempty
1454 $ hg ci -qAl - <<EOF
1497 $ hg ci -qAl - <<EOF
1455 > blah blah
1498 > blah blah
1456 > diff blah
1499 > diff blah
1457 > blah blah
1500 > blah blah
1458 > EOF
1501 > EOF
1459 $ hg --config diff.git=1 log -pv
1502 $ hg --config diff.git=1 log -pv
1460 changeset: 0:c6ef204ef767
1503 changeset: 0:c6ef204ef767
1461 tag: tip
1504 tag: tip
1462 user: test
1505 user: test
1463 date: Thu Jan 01 00:00:00 1970 +0000
1506 date: Thu Jan 01 00:00:00 1970 +0000
1464 files: empty nonempty
1507 files: empty nonempty
1465 description:
1508 description:
1466 blah blah
1509 blah blah
1467 diff blah
1510 diff blah
1468 blah blah
1511 blah blah
1469
1512
1470
1513
1471 diff --git a/empty b/empty
1514 diff --git a/empty b/empty
1472 new file mode 100644
1515 new file mode 100644
1473 diff --git a/nonempty b/nonempty
1516 diff --git a/nonempty b/nonempty
1474 new file mode 100644
1517 new file mode 100644
1475 --- /dev/null
1518 --- /dev/null
1476 +++ b/nonempty
1519 +++ b/nonempty
1477 @@ -0,0 +1,1 @@
1520 @@ -0,0 +1,1 @@
1478 +nonempty
1521 +nonempty
1479
1522
1480
1523
1481 (without --git, empty file is lost, but commit message should be preserved)
1524 (without --git, empty file is lost, but commit message should be preserved)
1482
1525
1483 $ hg init plain
1526 $ hg init plain
1484 $ hg export 0 | hg -R plain import -
1527 $ hg export 0 | hg -R plain import -
1485 applying patch from stdin
1528 applying patch from stdin
1486 $ hg --config diff.git=1 -R plain log -pv
1529 $ hg --config diff.git=1 -R plain log -pv
1487 changeset: 0:60a2d231e71f
1530 changeset: 0:60a2d231e71f
1488 tag: tip
1531 tag: tip
1489 user: test
1532 user: test
1490 date: Thu Jan 01 00:00:00 1970 +0000
1533 date: Thu Jan 01 00:00:00 1970 +0000
1491 files: nonempty
1534 files: nonempty
1492 description:
1535 description:
1493 blah blah
1536 blah blah
1494 diff blah
1537 diff blah
1495 blah blah
1538 blah blah
1496
1539
1497
1540
1498 diff --git a/nonempty b/nonempty
1541 diff --git a/nonempty b/nonempty
1499 new file mode 100644
1542 new file mode 100644
1500 --- /dev/null
1543 --- /dev/null
1501 +++ b/nonempty
1544 +++ b/nonempty
1502 @@ -0,0 +1,1 @@
1545 @@ -0,0 +1,1 @@
1503 +nonempty
1546 +nonempty
1504
1547
1505
1548
1506 (with --git, patch contents should be fully preserved)
1549 (with --git, patch contents should be fully preserved)
1507
1550
1508 $ hg init git
1551 $ hg init git
1509 $ hg --config diff.git=1 export 0 | hg -R git import -
1552 $ hg --config diff.git=1 export 0 | hg -R git import -
1510 applying patch from stdin
1553 applying patch from stdin
1511 $ hg --config diff.git=1 -R git log -pv
1554 $ hg --config diff.git=1 -R git log -pv
1512 changeset: 0:c6ef204ef767
1555 changeset: 0:c6ef204ef767
1513 tag: tip
1556 tag: tip
1514 user: test
1557 user: test
1515 date: Thu Jan 01 00:00:00 1970 +0000
1558 date: Thu Jan 01 00:00:00 1970 +0000
1516 files: empty nonempty
1559 files: empty nonempty
1517 description:
1560 description:
1518 blah blah
1561 blah blah
1519 diff blah
1562 diff blah
1520 blah blah
1563 blah blah
1521
1564
1522
1565
1523 diff --git a/empty b/empty
1566 diff --git a/empty b/empty
1524 new file mode 100644
1567 new file mode 100644
1525 diff --git a/nonempty b/nonempty
1568 diff --git a/nonempty b/nonempty
1526 new file mode 100644
1569 new file mode 100644
1527 --- /dev/null
1570 --- /dev/null
1528 +++ b/nonempty
1571 +++ b/nonempty
1529 @@ -0,0 +1,1 @@
1572 @@ -0,0 +1,1 @@
1530 +nonempty
1573 +nonempty
1531
1574
1532
1575
1533 $ cd ..
1576 $ cd ..
1534
1577
1535 no segfault while importing a unified diff which start line is zero but chunk
1578 no segfault while importing a unified diff which start line is zero but chunk
1536 size is non-zero
1579 size is non-zero
1537
1580
1538 $ hg init startlinezero
1581 $ hg init startlinezero
1539 $ cd startlinezero
1582 $ cd startlinezero
1540 $ echo foo > foo
1583 $ echo foo > foo
1541 $ hg commit -Amfoo
1584 $ hg commit -Amfoo
1542 adding foo
1585 adding foo
1543
1586
1544 $ hg import --no-commit - << EOF
1587 $ hg import --no-commit - << EOF
1545 > diff a/foo b/foo
1588 > diff a/foo b/foo
1546 > --- a/foo
1589 > --- a/foo
1547 > +++ b/foo
1590 > +++ b/foo
1548 > @@ -0,1 +0,1 @@
1591 > @@ -0,1 +0,1 @@
1549 > foo
1592 > foo
1550 > EOF
1593 > EOF
1551 applying patch from stdin
1594 applying patch from stdin
1552
1595
1553 $ cd ..
1596 $ cd ..
1554
1597
1555 Test corner case involving fuzz and skew
1598 Test corner case involving fuzz and skew
1556
1599
1557 $ hg init morecornercases
1600 $ hg init morecornercases
1558 $ cd morecornercases
1601 $ cd morecornercases
1559
1602
1560 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1603 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1561 > diff --git a/a b/a
1604 > diff --git a/a b/a
1562 > --- a/a
1605 > --- a/a
1563 > +++ b/a
1606 > +++ b/a
1564 > @@ -1,0 +1,1 @@
1607 > @@ -1,0 +1,1 @@
1565 > +line
1608 > +line
1566 > EOF
1609 > EOF
1567
1610
1568 $ cat > 02-no-context-middle-of-file.diff <<EOF
1611 $ cat > 02-no-context-middle-of-file.diff <<EOF
1569 > diff --git a/a b/a
1612 > diff --git a/a b/a
1570 > --- a/a
1613 > --- a/a
1571 > +++ b/a
1614 > +++ b/a
1572 > @@ -1,1 +1,1 @@
1615 > @@ -1,1 +1,1 @@
1573 > -2
1616 > -2
1574 > +add some skew
1617 > +add some skew
1575 > @@ -2,0 +2,1 @@
1618 > @@ -2,0 +2,1 @@
1576 > +line
1619 > +line
1577 > EOF
1620 > EOF
1578
1621
1579 $ cat > 03-no-context-end-of-file.diff <<EOF
1622 $ cat > 03-no-context-end-of-file.diff <<EOF
1580 > diff --git a/a b/a
1623 > diff --git a/a b/a
1581 > --- a/a
1624 > --- a/a
1582 > +++ b/a
1625 > +++ b/a
1583 > @@ -10,0 +10,1 @@
1626 > @@ -10,0 +10,1 @@
1584 > +line
1627 > +line
1585 > EOF
1628 > EOF
1586
1629
1587 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1630 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1588 > diff --git a/a b/a
1631 > diff --git a/a b/a
1589 > --- a/a
1632 > --- a/a
1590 > +++ b/a
1633 > +++ b/a
1591 > @@ -1,1 +1,1 @@
1634 > @@ -1,1 +1,1 @@
1592 > -2
1635 > -2
1593 > +add some skew
1636 > +add some skew
1594 > @@ -2,2 +2,3 @@
1637 > @@ -2,2 +2,3 @@
1595 > not matching, should fuzz
1638 > not matching, should fuzz
1596 > ... a bit
1639 > ... a bit
1597 > +line
1640 > +line
1598 > EOF
1641 > EOF
1599
1642
1600 $ cat > a <<EOF
1643 $ cat > a <<EOF
1601 > 1
1644 > 1
1602 > 2
1645 > 2
1603 > 3
1646 > 3
1604 > 4
1647 > 4
1605 > EOF
1648 > EOF
1606 $ hg ci -Am adda a
1649 $ hg ci -Am adda a
1607 $ for p in *.diff; do
1650 $ for p in *.diff; do
1608 > hg import -v --no-commit $p
1651 > hg import -v --no-commit $p
1609 > cat a
1652 > cat a
1610 > hg revert -aqC a
1653 > hg revert -aqC a
1611 > # patch -p1 < $p
1654 > # patch -p1 < $p
1612 > # cat a
1655 > # cat a
1613 > # hg revert -aC a
1656 > # hg revert -aC a
1614 > done
1657 > done
1615 applying 01-no-context-beginning-of-file.diff
1658 applying 01-no-context-beginning-of-file.diff
1616 patching file a
1659 patching file a
1617 applied to working directory
1660 applied to working directory
1618 1
1661 1
1619 line
1662 line
1620 2
1663 2
1621 3
1664 3
1622 4
1665 4
1623 applying 02-no-context-middle-of-file.diff
1666 applying 02-no-context-middle-of-file.diff
1624 patching file a
1667 patching file a
1625 Hunk #1 succeeded at 2 (offset 1 lines).
1668 Hunk #1 succeeded at 2 (offset 1 lines).
1626 Hunk #2 succeeded at 4 (offset 1 lines).
1669 Hunk #2 succeeded at 4 (offset 1 lines).
1627 applied to working directory
1670 applied to working directory
1628 1
1671 1
1629 add some skew
1672 add some skew
1630 3
1673 3
1631 line
1674 line
1632 4
1675 4
1633 applying 03-no-context-end-of-file.diff
1676 applying 03-no-context-end-of-file.diff
1634 patching file a
1677 patching file a
1635 Hunk #1 succeeded at 5 (offset -6 lines).
1678 Hunk #1 succeeded at 5 (offset -6 lines).
1636 applied to working directory
1679 applied to working directory
1637 1
1680 1
1638 2
1681 2
1639 3
1682 3
1640 4
1683 4
1641 line
1684 line
1642 applying 04-middle-of-file-completely-fuzzed.diff
1685 applying 04-middle-of-file-completely-fuzzed.diff
1643 patching file a
1686 patching file a
1644 Hunk #1 succeeded at 2 (offset 1 lines).
1687 Hunk #1 succeeded at 2 (offset 1 lines).
1645 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1688 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1646 applied to working directory
1689 applied to working directory
1647 1
1690 1
1648 add some skew
1691 add some skew
1649 3
1692 3
1650 4
1693 4
1651 line
1694 line
1652 $ cd ..
1695 $ cd ..
1653
1696
1654 Test partial application
1697 Test partial application
1655 ------------------------
1698 ------------------------
1656
1699
1657 prepare a stack of patches depending on each other
1700 prepare a stack of patches depending on each other
1658
1701
1659 $ hg init partial
1702 $ hg init partial
1660 $ cd partial
1703 $ cd partial
1661 $ cat << EOF > a
1704 $ cat << EOF > a
1662 > one
1705 > one
1663 > two
1706 > two
1664 > three
1707 > three
1665 > four
1708 > four
1666 > five
1709 > five
1667 > six
1710 > six
1668 > seven
1711 > seven
1669 > EOF
1712 > EOF
1670 $ hg add a
1713 $ hg add a
1671 $ echo 'b' > b
1714 $ echo 'b' > b
1672 $ hg add b
1715 $ hg add b
1673 $ hg commit -m 'initial' -u Babar
1716 $ hg commit -m 'initial' -u Babar
1674 $ cat << EOF > a
1717 $ cat << EOF > a
1675 > one
1718 > one
1676 > two
1719 > two
1677 > 3
1720 > 3
1678 > four
1721 > four
1679 > five
1722 > five
1680 > six
1723 > six
1681 > seven
1724 > seven
1682 > EOF
1725 > EOF
1683 $ hg commit -m 'three' -u Celeste
1726 $ hg commit -m 'three' -u Celeste
1684 $ cat << EOF > a
1727 $ cat << EOF > a
1685 > one
1728 > one
1686 > two
1729 > two
1687 > 3
1730 > 3
1688 > 4
1731 > 4
1689 > five
1732 > five
1690 > six
1733 > six
1691 > seven
1734 > seven
1692 > EOF
1735 > EOF
1693 $ hg commit -m 'four' -u Rataxes
1736 $ hg commit -m 'four' -u Rataxes
1694 $ cat << EOF > a
1737 $ cat << EOF > a
1695 > one
1738 > one
1696 > two
1739 > two
1697 > 3
1740 > 3
1698 > 4
1741 > 4
1699 > 5
1742 > 5
1700 > six
1743 > six
1701 > seven
1744 > seven
1702 > EOF
1745 > EOF
1703 $ echo bb >> b
1746 $ echo bb >> b
1704 $ hg commit -m 'five' -u Arthur
1747 $ hg commit -m 'five' -u Arthur
1705 $ echo 'Babar' > jungle
1748 $ echo 'Babar' > jungle
1706 $ hg add jungle
1749 $ hg add jungle
1707 $ hg ci -m 'jungle' -u Zephir
1750 $ hg ci -m 'jungle' -u Zephir
1708 $ echo 'Celeste' >> jungle
1751 $ echo 'Celeste' >> jungle
1709 $ hg ci -m 'extended jungle' -u Cornelius
1752 $ hg ci -m 'extended jungle' -u Cornelius
1710 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1753 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1711 @ extended jungle [Cornelius] 1: +1/-0
1754 @ extended jungle [Cornelius] 1: +1/-0
1712 |
1755 |
1713 o jungle [Zephir] 1: +1/-0
1756 o jungle [Zephir] 1: +1/-0
1714 |
1757 |
1715 o five [Arthur] 2: +2/-1
1758 o five [Arthur] 2: +2/-1
1716 |
1759 |
1717 o four [Rataxes] 1: +1/-1
1760 o four [Rataxes] 1: +1/-1
1718 |
1761 |
1719 o three [Celeste] 1: +1/-1
1762 o three [Celeste] 1: +1/-1
1720 |
1763 |
1721 o initial [Babar] 2: +8/-0
1764 o initial [Babar] 2: +8/-0
1722
1765
1723 Adding those config options should not change the output of diffstat. Bugfix #4755.
1766 Adding those config options should not change the output of diffstat. Bugfix #4755.
1724
1767
1725 $ hg log -r . --template '{diffstat}\n'
1768 $ hg log -r . --template '{diffstat}\n'
1726 1: +1/-0
1769 1: +1/-0
1727 $ hg log -r . --template '{diffstat}\n' --config diff.git=1 \
1770 $ hg log -r . --template '{diffstat}\n' --config diff.git=1 \
1728 > --config diff.noprefix=1
1771 > --config diff.noprefix=1
1729 1: +1/-0
1772 1: +1/-0
1730
1773
1731 Importing with some success and some errors:
1774 Importing with some success and some errors:
1732
1775
1733 $ hg update --rev 'desc(initial)'
1776 $ hg update --rev 'desc(initial)'
1734 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1777 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1735 $ hg export --rev 'desc(five)' | hg import --partial -
1778 $ hg export --rev 'desc(five)' | hg import --partial -
1736 applying patch from stdin
1779 applying patch from stdin
1737 patching file a
1780 patching file a
1738 Hunk #1 FAILED at 1
1781 Hunk #1 FAILED at 1
1739 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1782 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1740 patch applied partially
1783 patch applied partially
1741 (fix the .rej files and run `hg commit --amend`)
1784 (fix the .rej files and run `hg commit --amend`)
1742 [1]
1785 [1]
1743
1786
1744 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1787 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1745 @ five [Arthur] 1: +1/-0
1788 @ five [Arthur] 1: +1/-0
1746 |
1789 |
1747 | o extended jungle [Cornelius] 1: +1/-0
1790 | o extended jungle [Cornelius] 1: +1/-0
1748 | |
1791 | |
1749 | o jungle [Zephir] 1: +1/-0
1792 | o jungle [Zephir] 1: +1/-0
1750 | |
1793 | |
1751 | o five [Arthur] 2: +2/-1
1794 | o five [Arthur] 2: +2/-1
1752 | |
1795 | |
1753 | o four [Rataxes] 1: +1/-1
1796 | o four [Rataxes] 1: +1/-1
1754 | |
1797 | |
1755 | o three [Celeste] 1: +1/-1
1798 | o three [Celeste] 1: +1/-1
1756 |/
1799 |/
1757 o initial [Babar] 2: +8/-0
1800 o initial [Babar] 2: +8/-0
1758
1801
1759 $ hg export
1802 $ hg export
1760 # HG changeset patch
1803 # HG changeset patch
1761 # User Arthur
1804 # User Arthur
1762 # Date 0 0
1805 # Date 0 0
1763 # Thu Jan 01 00:00:00 1970 +0000
1806 # Thu Jan 01 00:00:00 1970 +0000
1764 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1807 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1765 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1808 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1766 five
1809 five
1767
1810
1768 diff -r 8e4f0351909e -r 26e6446bb252 b
1811 diff -r 8e4f0351909e -r 26e6446bb252 b
1769 --- a/b Thu Jan 01 00:00:00 1970 +0000
1812 --- a/b Thu Jan 01 00:00:00 1970 +0000
1770 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1813 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1771 @@ -1,1 +1,2 @@
1814 @@ -1,1 +1,2 @@
1772 b
1815 b
1773 +bb
1816 +bb
1774 $ hg status -c .
1817 $ hg status -c .
1775 C a
1818 C a
1776 C b
1819 C b
1777 $ ls
1820 $ ls
1778 a
1821 a
1779 a.rej
1822 a.rej
1780 b
1823 b
1781
1824
1782 Importing with zero success:
1825 Importing with zero success:
1783
1826
1784 $ hg update --rev 'desc(initial)'
1827 $ hg update --rev 'desc(initial)'
1785 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1828 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1786 $ hg export --rev 'desc(four)' | hg import --partial -
1829 $ hg export --rev 'desc(four)' | hg import --partial -
1787 applying patch from stdin
1830 applying patch from stdin
1788 patching file a
1831 patching file a
1789 Hunk #1 FAILED at 0
1832 Hunk #1 FAILED at 0
1790 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1833 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1791 patch applied partially
1834 patch applied partially
1792 (fix the .rej files and run `hg commit --amend`)
1835 (fix the .rej files and run `hg commit --amend`)
1793 [1]
1836 [1]
1794
1837
1795 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1838 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1796 @ four [Rataxes] 0: +0/-0
1839 @ four [Rataxes] 0: +0/-0
1797 |
1840 |
1798 | o five [Arthur] 1: +1/-0
1841 | o five [Arthur] 1: +1/-0
1799 |/
1842 |/
1800 | o extended jungle [Cornelius] 1: +1/-0
1843 | o extended jungle [Cornelius] 1: +1/-0
1801 | |
1844 | |
1802 | o jungle [Zephir] 1: +1/-0
1845 | o jungle [Zephir] 1: +1/-0
1803 | |
1846 | |
1804 | o five [Arthur] 2: +2/-1
1847 | o five [Arthur] 2: +2/-1
1805 | |
1848 | |
1806 | o four [Rataxes] 1: +1/-1
1849 | o four [Rataxes] 1: +1/-1
1807 | |
1850 | |
1808 | o three [Celeste] 1: +1/-1
1851 | o three [Celeste] 1: +1/-1
1809 |/
1852 |/
1810 o initial [Babar] 2: +8/-0
1853 o initial [Babar] 2: +8/-0
1811
1854
1812 $ hg export
1855 $ hg export
1813 # HG changeset patch
1856 # HG changeset patch
1814 # User Rataxes
1857 # User Rataxes
1815 # Date 0 0
1858 # Date 0 0
1816 # Thu Jan 01 00:00:00 1970 +0000
1859 # Thu Jan 01 00:00:00 1970 +0000
1817 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1860 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1818 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1861 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1819 four
1862 four
1820
1863
1821 $ hg status -c .
1864 $ hg status -c .
1822 C a
1865 C a
1823 C b
1866 C b
1824 $ ls
1867 $ ls
1825 a
1868 a
1826 a.rej
1869 a.rej
1827 b
1870 b
1828
1871
1829 Importing with unknown file:
1872 Importing with unknown file:
1830
1873
1831 $ hg update --rev 'desc(initial)'
1874 $ hg update --rev 'desc(initial)'
1832 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1875 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1833 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1876 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1834 applying patch from stdin
1877 applying patch from stdin
1835 unable to find 'jungle' for patching
1878 unable to find 'jungle' for patching
1836 (use '--prefix' to apply patch relative to the current directory)
1879 (use '--prefix' to apply patch relative to the current directory)
1837 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1880 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1838 patch applied partially
1881 patch applied partially
1839 (fix the .rej files and run `hg commit --amend`)
1882 (fix the .rej files and run `hg commit --amend`)
1840 [1]
1883 [1]
1841
1884
1842 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1885 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1843 @ extended jungle [Cornelius] 0: +0/-0
1886 @ extended jungle [Cornelius] 0: +0/-0
1844 |
1887 |
1845 | o four [Rataxes] 0: +0/-0
1888 | o four [Rataxes] 0: +0/-0
1846 |/
1889 |/
1847 | o five [Arthur] 1: +1/-0
1890 | o five [Arthur] 1: +1/-0
1848 |/
1891 |/
1849 | o extended jungle [Cornelius] 1: +1/-0
1892 | o extended jungle [Cornelius] 1: +1/-0
1850 | |
1893 | |
1851 | o jungle [Zephir] 1: +1/-0
1894 | o jungle [Zephir] 1: +1/-0
1852 | |
1895 | |
1853 | o five [Arthur] 2: +2/-1
1896 | o five [Arthur] 2: +2/-1
1854 | |
1897 | |
1855 | o four [Rataxes] 1: +1/-1
1898 | o four [Rataxes] 1: +1/-1
1856 | |
1899 | |
1857 | o three [Celeste] 1: +1/-1
1900 | o three [Celeste] 1: +1/-1
1858 |/
1901 |/
1859 o initial [Babar] 2: +8/-0
1902 o initial [Babar] 2: +8/-0
1860
1903
1861 $ hg export
1904 $ hg export
1862 # HG changeset patch
1905 # HG changeset patch
1863 # User Cornelius
1906 # User Cornelius
1864 # Date 0 0
1907 # Date 0 0
1865 # Thu Jan 01 00:00:00 1970 +0000
1908 # Thu Jan 01 00:00:00 1970 +0000
1866 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1909 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1867 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1910 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1868 extended jungle
1911 extended jungle
1869
1912
1870 $ hg status -c .
1913 $ hg status -c .
1871 C a
1914 C a
1872 C b
1915 C b
1873 $ ls
1916 $ ls
1874 a
1917 a
1875 a.rej
1918 a.rej
1876 b
1919 b
1877 jungle.rej
1920 jungle.rej
1878
1921
1879 Importing multiple failing patches:
1922 Importing multiple failing patches:
1880
1923
1881 $ hg update --rev 'desc(initial)'
1924 $ hg update --rev 'desc(initial)'
1882 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1925 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1883 $ echo 'B' > b # just to make another commit
1926 $ echo 'B' > b # just to make another commit
1884 $ hg commit -m "a new base"
1927 $ hg commit -m "a new base"
1885 created new head
1928 created new head
1886 $ hg export --rev 'desc("four") + desc("extended jungle")' | hg import --partial -
1929 $ hg export --rev 'desc("four") + desc("extended jungle")' | hg import --partial -
1887 applying patch from stdin
1930 applying patch from stdin
1888 patching file a
1931 patching file a
1889 Hunk #1 FAILED at 0
1932 Hunk #1 FAILED at 0
1890 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1933 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1891 patch applied partially
1934 patch applied partially
1892 (fix the .rej files and run `hg commit --amend`)
1935 (fix the .rej files and run `hg commit --amend`)
1893 [1]
1936 [1]
1894 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1937 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1895 @ four [Rataxes] 0: +0/-0
1938 @ four [Rataxes] 0: +0/-0
1896 |
1939 |
1897 o a new base [test] 1: +1/-1
1940 o a new base [test] 1: +1/-1
1898 |
1941 |
1899 | o extended jungle [Cornelius] 0: +0/-0
1942 | o extended jungle [Cornelius] 0: +0/-0
1900 |/
1943 |/
1901 | o four [Rataxes] 0: +0/-0
1944 | o four [Rataxes] 0: +0/-0
1902 |/
1945 |/
1903 | o five [Arthur] 1: +1/-0
1946 | o five [Arthur] 1: +1/-0
1904 |/
1947 |/
1905 | o extended jungle [Cornelius] 1: +1/-0
1948 | o extended jungle [Cornelius] 1: +1/-0
1906 | |
1949 | |
1907 | o jungle [Zephir] 1: +1/-0
1950 | o jungle [Zephir] 1: +1/-0
1908 | |
1951 | |
1909 | o five [Arthur] 2: +2/-1
1952 | o five [Arthur] 2: +2/-1
1910 | |
1953 | |
1911 | o four [Rataxes] 1: +1/-1
1954 | o four [Rataxes] 1: +1/-1
1912 | |
1955 | |
1913 | o three [Celeste] 1: +1/-1
1956 | o three [Celeste] 1: +1/-1
1914 |/
1957 |/
1915 o initial [Babar] 2: +8/-0
1958 o initial [Babar] 2: +8/-0
1916
1959
1917 $ hg export
1960 $ hg export
1918 # HG changeset patch
1961 # HG changeset patch
1919 # User Rataxes
1962 # User Rataxes
1920 # Date 0 0
1963 # Date 0 0
1921 # Thu Jan 01 00:00:00 1970 +0000
1964 # Thu Jan 01 00:00:00 1970 +0000
1922 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1965 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1923 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1966 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1924 four
1967 four
1925
1968
1926 $ hg status -c .
1969 $ hg status -c .
1927 C a
1970 C a
1928 C b
1971 C b
1929
1972
1930 Importing some extra header
1973 Importing some extra header
1931 ===========================
1974 ===========================
1932
1975
1933 $ cat > $TESTTMP/parseextra.py <<EOF
1976 $ cat > $TESTTMP/parseextra.py <<EOF
1934 > import mercurial.cmdutil
1977 > import mercurial.cmdutil
1935 > import mercurial.patch
1978 > import mercurial.patch
1936 >
1979 >
1937 > def processfoo(repo, data, extra, opts):
1980 > def processfoo(repo, data, extra, opts):
1938 > if b'foo' in data:
1981 > if b'foo' in data:
1939 > extra[b'foo'] = data[b'foo']
1982 > extra[b'foo'] = data[b'foo']
1940 > def postimport(ctx):
1983 > def postimport(ctx):
1941 > if b'foo' in ctx.extra():
1984 > if b'foo' in ctx.extra():
1942 > ctx.repo().ui.write(b'imported-foo: %s\n' % ctx.extra()[b'foo'])
1985 > ctx.repo().ui.write(b'imported-foo: %s\n' % ctx.extra()[b'foo'])
1943 >
1986 >
1944 > mercurial.patch.patchheadermap.append((b'Foo', b'foo'))
1987 > mercurial.patch.patchheadermap.append((b'Foo', b'foo'))
1945 > mercurial.cmdutil.extrapreimport.append(b'foo')
1988 > mercurial.cmdutil.extrapreimport.append(b'foo')
1946 > mercurial.cmdutil.extrapreimportmap[b'foo'] = processfoo
1989 > mercurial.cmdutil.extrapreimportmap[b'foo'] = processfoo
1947 > mercurial.cmdutil.extrapostimport.append(b'foo')
1990 > mercurial.cmdutil.extrapostimport.append(b'foo')
1948 > mercurial.cmdutil.extrapostimportmap[b'foo'] = postimport
1991 > mercurial.cmdutil.extrapostimportmap[b'foo'] = postimport
1949 > EOF
1992 > EOF
1950 $ cat >> $HGRCPATH <<EOF
1993 $ cat >> $HGRCPATH <<EOF
1951 > [extensions]
1994 > [extensions]
1952 > parseextra=$TESTTMP/parseextra.py
1995 > parseextra=$TESTTMP/parseextra.py
1953 > EOF
1996 > EOF
1954 $ hg up -C tip
1997 $ hg up -C tip
1955 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1998 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1956 $ cat > $TESTTMP/foo.patch <<EOF
1999 $ cat > $TESTTMP/foo.patch <<EOF
1957 > # HG changeset patch
2000 > # HG changeset patch
1958 > # User Rataxes
2001 > # User Rataxes
1959 > # Date 0 0
2002 > # Date 0 0
1960 > # Thu Jan 01 00:00:00 1970 +0000
2003 > # Thu Jan 01 00:00:00 1970 +0000
1961 > # Foo bar
2004 > # Foo bar
1962 > height
2005 > height
1963 >
2006 >
1964 > --- a/a Thu Jan 01 00:00:00 1970 +0000
2007 > --- a/a Thu Jan 01 00:00:00 1970 +0000
1965 > +++ b/a Wed Oct 07 09:17:44 2015 +0000
2008 > +++ b/a Wed Oct 07 09:17:44 2015 +0000
1966 > @@ -5,3 +5,4 @@
2009 > @@ -5,3 +5,4 @@
1967 > five
2010 > five
1968 > six
2011 > six
1969 > seven
2012 > seven
1970 > +heigt
2013 > +heigt
1971 > EOF
2014 > EOF
1972 $ hg import $TESTTMP/foo.patch
2015 $ hg import $TESTTMP/foo.patch
1973 applying $TESTTMP/foo.patch
2016 applying $TESTTMP/foo.patch
1974 imported-foo: bar
2017 imported-foo: bar
1975 $ hg log --debug -r . | grep extra
2018 $ hg log --debug -r . | grep extra
1976 extra: branch=default
2019 extra: branch=default
1977 extra: foo=bar
2020 extra: foo=bar
1978
2021
1979 Warn the user that paths are relative to the root of
2022 Warn the user that paths are relative to the root of
1980 repository when file not found for patching
2023 repository when file not found for patching
1981
2024
1982 $ mkdir filedir
2025 $ mkdir filedir
1983 $ echo "file1" >> filedir/file1
2026 $ echo "file1" >> filedir/file1
1984 $ hg add filedir/file1
2027 $ hg add filedir/file1
1985 $ hg commit -m "file1"
2028 $ hg commit -m "file1"
1986 $ cd filedir
2029 $ cd filedir
1987 $ hg import -p 2 - <<EOF
2030 $ hg import -p 2 - <<EOF
1988 > # HG changeset patch
2031 > # HG changeset patch
1989 > # User test
2032 > # User test
1990 > # Date 0 0
2033 > # Date 0 0
1991 > file2
2034 > file2
1992 >
2035 >
1993 > diff --git a/filedir/file1 b/filedir/file1
2036 > diff --git a/filedir/file1 b/filedir/file1
1994 > --- a/filedir/file1
2037 > --- a/filedir/file1
1995 > +++ b/filedir/file1
2038 > +++ b/filedir/file1
1996 > @@ -1,1 +1,2 @@
2039 > @@ -1,1 +1,2 @@
1997 > file1
2040 > file1
1998 > +file2
2041 > +file2
1999 > EOF
2042 > EOF
2000 applying patch from stdin
2043 applying patch from stdin
2001 unable to find 'file1' for patching
2044 unable to find 'file1' for patching
2002 (use '--prefix' to apply patch relative to the current directory)
2045 (use '--prefix' to apply patch relative to the current directory)
2003 1 out of 1 hunks FAILED -- saving rejects to file file1.rej
2046 1 out of 1 hunks FAILED -- saving rejects to file file1.rej
2004 abort: patch failed to apply
2047 abort: patch failed to apply
2005 [255]
2048 [255]
2006
2049
2007 test import crash (issue5375)
2050 test import crash (issue5375)
2008 $ cd ..
2051 $ cd ..
2009 $ hg init repo
2052 $ hg init repo
2010 $ cd repo
2053 $ cd repo
2011 $ printf "diff --git a/a b/b\nrename from a\nrename to b" | hg import -
2054 $ printf "diff --git a/a b/b\nrename from a\nrename to b" | hg import -
2012 applying patch from stdin
2055 applying patch from stdin
2013 a not tracked!
2056 a not tracked!
2014 abort: source file 'a' does not exist
2057 abort: source file 'a' does not exist
2015 [255]
2058 [255]
2016
2059
2017 test immature end of hunk
2060 test immature end of hunk
2018
2061
2019 $ hg import - <<'EOF'
2062 $ hg import - <<'EOF'
2020 > diff --git a/foo b/foo
2063 > diff --git a/foo b/foo
2021 > --- a/foo
2064 > --- a/foo
2022 > --- b/foo
2065 > --- b/foo
2023 > @@ -0,0 +1,1 @@
2066 > @@ -0,0 +1,1 @@
2024 > EOF
2067 > EOF
2025 applying patch from stdin
2068 applying patch from stdin
2026 abort: bad hunk #1: incomplete hunk
2069 abort: bad hunk #1: incomplete hunk
2027 [255]
2070 [255]
2028
2071
2029 $ hg import - <<'EOF'
2072 $ hg import - <<'EOF'
2030 > diff --git a/foo b/foo
2073 > diff --git a/foo b/foo
2031 > --- a/foo
2074 > --- a/foo
2032 > --- b/foo
2075 > --- b/foo
2033 > @@ -0,0 +1,1 @@
2076 > @@ -0,0 +1,1 @@
2034 > \ No newline at end of file
2077 > \ No newline at end of file
2035 > EOF
2078 > EOF
2036 applying patch from stdin
2079 applying patch from stdin
2037 abort: bad hunk #1: incomplete hunk
2080 abort: bad hunk #1: incomplete hunk
2038 [255]
2081 [255]
General Comments 0
You need to be logged in to leave comments. Login now