##// END OF EJS Templates
diff: use cmdutil.check_at_most_one_arg() for checking --rev/--change...
Martin von Zweigbergk -
r45322:aac816f5 default
parent child Browse files
Show More
@@ -1,722 +1,720 b''
1 # extdiff.py - external diff program support for mercurial
1 # extdiff.py - external diff program support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.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 '''command to allow external programs to compare revisions
8 '''command to allow external programs to compare revisions
9
9
10 The extdiff Mercurial extension allows you to use external programs
10 The extdiff Mercurial extension allows you to use external programs
11 to compare revisions, or revision with working directory. The external
11 to compare revisions, or revision with working directory. The external
12 diff programs are called with a configurable set of options and two
12 diff programs are called with a configurable set of options and two
13 non-option arguments: paths to directories containing snapshots of
13 non-option arguments: paths to directories containing snapshots of
14 files to compare.
14 files to compare.
15
15
16 If there is more than one file being compared and the "child" revision
16 If there is more than one file being compared and the "child" revision
17 is the working directory, any modifications made in the external diff
17 is the working directory, any modifications made in the external diff
18 program will be copied back to the working directory from the temporary
18 program will be copied back to the working directory from the temporary
19 directory.
19 directory.
20
20
21 The extdiff extension also allows you to configure new diff commands, so
21 The extdiff extension also allows you to configure new diff commands, so
22 you do not need to type :hg:`extdiff -p kdiff3` always. ::
22 you do not need to type :hg:`extdiff -p kdiff3` always. ::
23
23
24 [extdiff]
24 [extdiff]
25 # add new command that runs GNU diff(1) in 'context diff' mode
25 # add new command that runs GNU diff(1) in 'context diff' mode
26 cdiff = gdiff -Nprc5
26 cdiff = gdiff -Nprc5
27 ## or the old way:
27 ## or the old way:
28 #cmd.cdiff = gdiff
28 #cmd.cdiff = gdiff
29 #opts.cdiff = -Nprc5
29 #opts.cdiff = -Nprc5
30
30
31 # add new command called meld, runs meld (no need to name twice). If
31 # add new command called meld, runs meld (no need to name twice). If
32 # the meld executable is not available, the meld tool in [merge-tools]
32 # the meld executable is not available, the meld tool in [merge-tools]
33 # will be used, if available
33 # will be used, if available
34 meld =
34 meld =
35
35
36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
37 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
37 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
38 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
38 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
39 # your .vimrc
39 # your .vimrc
40 vimdiff = gvim -f "+next" \\
40 vimdiff = gvim -f "+next" \\
41 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
41 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
42
42
43 Tool arguments can include variables that are expanded at runtime::
43 Tool arguments can include variables that are expanded at runtime::
44
44
45 $parent1, $plabel1 - filename, descriptive label of first parent
45 $parent1, $plabel1 - filename, descriptive label of first parent
46 $child, $clabel - filename, descriptive label of child revision
46 $child, $clabel - filename, descriptive label of child revision
47 $parent2, $plabel2 - filename, descriptive label of second parent
47 $parent2, $plabel2 - filename, descriptive label of second parent
48 $root - repository root
48 $root - repository root
49 $parent is an alias for $parent1.
49 $parent is an alias for $parent1.
50
50
51 The extdiff extension will look in your [diff-tools] and [merge-tools]
51 The extdiff extension will look in your [diff-tools] and [merge-tools]
52 sections for diff tool arguments, when none are specified in [extdiff].
52 sections for diff tool arguments, when none are specified in [extdiff].
53
53
54 ::
54 ::
55
55
56 [extdiff]
56 [extdiff]
57 kdiff3 =
57 kdiff3 =
58
58
59 [diff-tools]
59 [diff-tools]
60 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
60 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
61
61
62 If a program has a graphical interface, it might be interesting to tell
62 If a program has a graphical interface, it might be interesting to tell
63 Mercurial about it. It will prevent the program from being mistakenly
63 Mercurial about it. It will prevent the program from being mistakenly
64 used in a terminal-only environment (such as an SSH terminal session),
64 used in a terminal-only environment (such as an SSH terminal session),
65 and will make :hg:`extdiff --per-file` open multiple file diffs at once
65 and will make :hg:`extdiff --per-file` open multiple file diffs at once
66 instead of one by one (if you still want to open file diffs one by one,
66 instead of one by one (if you still want to open file diffs one by one,
67 you can use the --confirm option).
67 you can use the --confirm option).
68
68
69 Declaring that a tool has a graphical interface can be done with the
69 Declaring that a tool has a graphical interface can be done with the
70 ``gui`` flag next to where ``diffargs`` are specified:
70 ``gui`` flag next to where ``diffargs`` are specified:
71
71
72 ::
72 ::
73
73
74 [diff-tools]
74 [diff-tools]
75 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
75 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
76 kdiff3.gui = true
76 kdiff3.gui = true
77
77
78 You can use -I/-X and list of file or directory names like normal
78 You can use -I/-X and list of file or directory names like normal
79 :hg:`diff` command. The extdiff extension makes snapshots of only
79 :hg:`diff` command. The extdiff extension makes snapshots of only
80 needed files, so running the external diff program will actually be
80 needed files, so running the external diff program will actually be
81 pretty fast (at least faster than having to compare the entire tree).
81 pretty fast (at least faster than having to compare the entire tree).
82 '''
82 '''
83
83
84 from __future__ import absolute_import
84 from __future__ import absolute_import
85
85
86 import os
86 import os
87 import re
87 import re
88 import shutil
88 import shutil
89 import stat
89 import stat
90 import subprocess
90 import subprocess
91
91
92 from mercurial.i18n import _
92 from mercurial.i18n import _
93 from mercurial.node import (
93 from mercurial.node import (
94 nullid,
94 nullid,
95 short,
95 short,
96 )
96 )
97 from mercurial import (
97 from mercurial import (
98 archival,
98 archival,
99 cmdutil,
99 cmdutil,
100 encoding,
100 encoding,
101 error,
101 error,
102 filemerge,
102 filemerge,
103 formatter,
103 formatter,
104 pycompat,
104 pycompat,
105 registrar,
105 registrar,
106 scmutil,
106 scmutil,
107 util,
107 util,
108 )
108 )
109 from mercurial.utils import (
109 from mercurial.utils import (
110 procutil,
110 procutil,
111 stringutil,
111 stringutil,
112 )
112 )
113
113
114 cmdtable = {}
114 cmdtable = {}
115 command = registrar.command(cmdtable)
115 command = registrar.command(cmdtable)
116
116
117 configtable = {}
117 configtable = {}
118 configitem = registrar.configitem(configtable)
118 configitem = registrar.configitem(configtable)
119
119
120 configitem(
120 configitem(
121 b'extdiff', br'opts\..*', default=b'', generic=True,
121 b'extdiff', br'opts\..*', default=b'', generic=True,
122 )
122 )
123
123
124 configitem(
124 configitem(
125 b'extdiff', br'gui\..*', generic=True,
125 b'extdiff', br'gui\..*', generic=True,
126 )
126 )
127
127
128 configitem(
128 configitem(
129 b'diff-tools', br'.*\.diffargs$', default=None, generic=True,
129 b'diff-tools', br'.*\.diffargs$', default=None, generic=True,
130 )
130 )
131
131
132 configitem(
132 configitem(
133 b'diff-tools', br'.*\.gui$', generic=True,
133 b'diff-tools', br'.*\.gui$', generic=True,
134 )
134 )
135
135
136 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
136 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
137 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
137 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
138 # be specifying the version(s) of Mercurial they are tested with, or
138 # be specifying the version(s) of Mercurial they are tested with, or
139 # leave the attribute unspecified.
139 # leave the attribute unspecified.
140 testedwith = b'ships-with-hg-core'
140 testedwith = b'ships-with-hg-core'
141
141
142
142
143 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
143 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
144 '''snapshot files as of some revision
144 '''snapshot files as of some revision
145 if not using snapshot, -I/-X does not work and recursive diff
145 if not using snapshot, -I/-X does not work and recursive diff
146 in tools like kdiff3 and meld displays too many files.'''
146 in tools like kdiff3 and meld displays too many files.'''
147 dirname = os.path.basename(repo.root)
147 dirname = os.path.basename(repo.root)
148 if dirname == b"":
148 if dirname == b"":
149 dirname = b"root"
149 dirname = b"root"
150 if node is not None:
150 if node is not None:
151 dirname = b'%s.%s' % (dirname, short(node))
151 dirname = b'%s.%s' % (dirname, short(node))
152 base = os.path.join(tmproot, dirname)
152 base = os.path.join(tmproot, dirname)
153 os.mkdir(base)
153 os.mkdir(base)
154 fnsandstat = []
154 fnsandstat = []
155
155
156 if node is not None:
156 if node is not None:
157 ui.note(
157 ui.note(
158 _(b'making snapshot of %d files from rev %s\n')
158 _(b'making snapshot of %d files from rev %s\n')
159 % (len(files), short(node))
159 % (len(files), short(node))
160 )
160 )
161 else:
161 else:
162 ui.note(
162 ui.note(
163 _(b'making snapshot of %d files from working directory\n')
163 _(b'making snapshot of %d files from working directory\n')
164 % (len(files))
164 % (len(files))
165 )
165 )
166
166
167 if files:
167 if files:
168 repo.ui.setconfig(b"ui", b"archivemeta", False)
168 repo.ui.setconfig(b"ui", b"archivemeta", False)
169
169
170 archival.archive(
170 archival.archive(
171 repo,
171 repo,
172 base,
172 base,
173 node,
173 node,
174 b'files',
174 b'files',
175 match=scmutil.matchfiles(repo, files),
175 match=scmutil.matchfiles(repo, files),
176 subrepos=listsubrepos,
176 subrepos=listsubrepos,
177 )
177 )
178
178
179 for fn in sorted(files):
179 for fn in sorted(files):
180 wfn = util.pconvert(fn)
180 wfn = util.pconvert(fn)
181 ui.note(b' %s\n' % wfn)
181 ui.note(b' %s\n' % wfn)
182
182
183 if node is None:
183 if node is None:
184 dest = os.path.join(base, wfn)
184 dest = os.path.join(base, wfn)
185
185
186 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest)))
186 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest)))
187 return dirname, fnsandstat
187 return dirname, fnsandstat
188
188
189
189
190 def formatcmdline(
190 def formatcmdline(
191 cmdline,
191 cmdline,
192 repo_root,
192 repo_root,
193 do3way,
193 do3way,
194 parent1,
194 parent1,
195 plabel1,
195 plabel1,
196 parent2,
196 parent2,
197 plabel2,
197 plabel2,
198 child,
198 child,
199 clabel,
199 clabel,
200 ):
200 ):
201 # Function to quote file/dir names in the argument string.
201 # Function to quote file/dir names in the argument string.
202 # When not operating in 3-way mode, an empty string is
202 # When not operating in 3-way mode, an empty string is
203 # returned for parent2
203 # returned for parent2
204 replace = {
204 replace = {
205 b'parent': parent1,
205 b'parent': parent1,
206 b'parent1': parent1,
206 b'parent1': parent1,
207 b'parent2': parent2,
207 b'parent2': parent2,
208 b'plabel1': plabel1,
208 b'plabel1': plabel1,
209 b'plabel2': plabel2,
209 b'plabel2': plabel2,
210 b'child': child,
210 b'child': child,
211 b'clabel': clabel,
211 b'clabel': clabel,
212 b'root': repo_root,
212 b'root': repo_root,
213 }
213 }
214
214
215 def quote(match):
215 def quote(match):
216 pre = match.group(2)
216 pre = match.group(2)
217 key = match.group(3)
217 key = match.group(3)
218 if not do3way and key == b'parent2':
218 if not do3way and key == b'parent2':
219 return pre
219 return pre
220 return pre + procutil.shellquote(replace[key])
220 return pre + procutil.shellquote(replace[key])
221
221
222 # Match parent2 first, so 'parent1?' will match both parent1 and parent
222 # Match parent2 first, so 'parent1?' will match both parent1 and parent
223 regex = (
223 regex = (
224 br'''(['"]?)([^\s'"$]*)'''
224 br'''(['"]?)([^\s'"$]*)'''
225 br'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1'
225 br'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1'
226 )
226 )
227 if not do3way and not re.search(regex, cmdline):
227 if not do3way and not re.search(regex, cmdline):
228 cmdline += b' $parent1 $child'
228 cmdline += b' $parent1 $child'
229 return re.sub(regex, quote, cmdline)
229 return re.sub(regex, quote, cmdline)
230
230
231
231
232 def _systembackground(cmd, environ=None, cwd=None):
232 def _systembackground(cmd, environ=None, cwd=None):
233 ''' like 'procutil.system', but returns the Popen object directly
233 ''' like 'procutil.system', but returns the Popen object directly
234 so we don't have to wait on it.
234 so we don't have to wait on it.
235 '''
235 '''
236 cmd = procutil.quotecommand(cmd)
236 cmd = procutil.quotecommand(cmd)
237 env = procutil.shellenviron(environ)
237 env = procutil.shellenviron(environ)
238 proc = subprocess.Popen(
238 proc = subprocess.Popen(
239 procutil.tonativestr(cmd),
239 procutil.tonativestr(cmd),
240 shell=True,
240 shell=True,
241 close_fds=procutil.closefds,
241 close_fds=procutil.closefds,
242 env=procutil.tonativeenv(env),
242 env=procutil.tonativeenv(env),
243 cwd=pycompat.rapply(procutil.tonativestr, cwd),
243 cwd=pycompat.rapply(procutil.tonativestr, cwd),
244 )
244 )
245 return proc
245 return proc
246
246
247
247
248 def _runperfilediff(
248 def _runperfilediff(
249 cmdline,
249 cmdline,
250 repo_root,
250 repo_root,
251 ui,
251 ui,
252 guitool,
252 guitool,
253 do3way,
253 do3way,
254 confirm,
254 confirm,
255 commonfiles,
255 commonfiles,
256 tmproot,
256 tmproot,
257 dir1a,
257 dir1a,
258 dir1b,
258 dir1b,
259 dir2root,
259 dir2root,
260 dir2,
260 dir2,
261 rev1a,
261 rev1a,
262 rev1b,
262 rev1b,
263 rev2,
263 rev2,
264 ):
264 ):
265 # Note that we need to sort the list of files because it was
265 # Note that we need to sort the list of files because it was
266 # built in an "unstable" way and it's annoying to get files in a
266 # built in an "unstable" way and it's annoying to get files in a
267 # random order, especially when "confirm" mode is enabled.
267 # random order, especially when "confirm" mode is enabled.
268 waitprocs = []
268 waitprocs = []
269 totalfiles = len(commonfiles)
269 totalfiles = len(commonfiles)
270 for idx, commonfile in enumerate(sorted(commonfiles)):
270 for idx, commonfile in enumerate(sorted(commonfiles)):
271 path1a = os.path.join(tmproot, dir1a, commonfile)
271 path1a = os.path.join(tmproot, dir1a, commonfile)
272 label1a = commonfile + rev1a
272 label1a = commonfile + rev1a
273 if not os.path.isfile(path1a):
273 if not os.path.isfile(path1a):
274 path1a = pycompat.osdevnull
274 path1a = pycompat.osdevnull
275
275
276 path1b = b''
276 path1b = b''
277 label1b = b''
277 label1b = b''
278 if do3way:
278 if do3way:
279 path1b = os.path.join(tmproot, dir1b, commonfile)
279 path1b = os.path.join(tmproot, dir1b, commonfile)
280 label1b = commonfile + rev1b
280 label1b = commonfile + rev1b
281 if not os.path.isfile(path1b):
281 if not os.path.isfile(path1b):
282 path1b = pycompat.osdevnull
282 path1b = pycompat.osdevnull
283
283
284 path2 = os.path.join(dir2root, dir2, commonfile)
284 path2 = os.path.join(dir2root, dir2, commonfile)
285 label2 = commonfile + rev2
285 label2 = commonfile + rev2
286
286
287 if confirm:
287 if confirm:
288 # Prompt before showing this diff
288 # Prompt before showing this diff
289 difffiles = _(b'diff %s (%d of %d)') % (
289 difffiles = _(b'diff %s (%d of %d)') % (
290 commonfile,
290 commonfile,
291 idx + 1,
291 idx + 1,
292 totalfiles,
292 totalfiles,
293 )
293 )
294 responses = _(
294 responses = _(
295 b'[Yns?]'
295 b'[Yns?]'
296 b'$$ &Yes, show diff'
296 b'$$ &Yes, show diff'
297 b'$$ &No, skip this diff'
297 b'$$ &No, skip this diff'
298 b'$$ &Skip remaining diffs'
298 b'$$ &Skip remaining diffs'
299 b'$$ &? (display help)'
299 b'$$ &? (display help)'
300 )
300 )
301 r = ui.promptchoice(b'%s %s' % (difffiles, responses))
301 r = ui.promptchoice(b'%s %s' % (difffiles, responses))
302 if r == 3: # ?
302 if r == 3: # ?
303 while r == 3:
303 while r == 3:
304 for c, t in ui.extractchoices(responses)[1]:
304 for c, t in ui.extractchoices(responses)[1]:
305 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
305 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
306 r = ui.promptchoice(b'%s %s' % (difffiles, responses))
306 r = ui.promptchoice(b'%s %s' % (difffiles, responses))
307 if r == 0: # yes
307 if r == 0: # yes
308 pass
308 pass
309 elif r == 1: # no
309 elif r == 1: # no
310 continue
310 continue
311 elif r == 2: # skip
311 elif r == 2: # skip
312 break
312 break
313
313
314 curcmdline = formatcmdline(
314 curcmdline = formatcmdline(
315 cmdline,
315 cmdline,
316 repo_root,
316 repo_root,
317 do3way=do3way,
317 do3way=do3way,
318 parent1=path1a,
318 parent1=path1a,
319 plabel1=label1a,
319 plabel1=label1a,
320 parent2=path1b,
320 parent2=path1b,
321 plabel2=label1b,
321 plabel2=label1b,
322 child=path2,
322 child=path2,
323 clabel=label2,
323 clabel=label2,
324 )
324 )
325
325
326 if confirm or not guitool:
326 if confirm or not guitool:
327 # Run the comparison program and wait for it to exit
327 # Run the comparison program and wait for it to exit
328 # before we show the next file.
328 # before we show the next file.
329 # This is because either we need to wait for confirmation
329 # This is because either we need to wait for confirmation
330 # from the user between each invocation, or because, as far
330 # from the user between each invocation, or because, as far
331 # as we know, the tool doesn't have a GUI, in which case
331 # as we know, the tool doesn't have a GUI, in which case
332 # we can't run multiple CLI programs at the same time.
332 # we can't run multiple CLI programs at the same time.
333 ui.debug(
333 ui.debug(
334 b'running %r in %s\n' % (pycompat.bytestr(curcmdline), tmproot)
334 b'running %r in %s\n' % (pycompat.bytestr(curcmdline), tmproot)
335 )
335 )
336 ui.system(curcmdline, cwd=tmproot, blockedtag=b'extdiff')
336 ui.system(curcmdline, cwd=tmproot, blockedtag=b'extdiff')
337 else:
337 else:
338 # Run the comparison program but don't wait, as we're
338 # Run the comparison program but don't wait, as we're
339 # going to rapid-fire each file diff and then wait on
339 # going to rapid-fire each file diff and then wait on
340 # the whole group.
340 # the whole group.
341 ui.debug(
341 ui.debug(
342 b'running %r in %s (backgrounded)\n'
342 b'running %r in %s (backgrounded)\n'
343 % (pycompat.bytestr(curcmdline), tmproot)
343 % (pycompat.bytestr(curcmdline), tmproot)
344 )
344 )
345 proc = _systembackground(curcmdline, cwd=tmproot)
345 proc = _systembackground(curcmdline, cwd=tmproot)
346 waitprocs.append(proc)
346 waitprocs.append(proc)
347
347
348 if waitprocs:
348 if waitprocs:
349 with ui.timeblockedsection(b'extdiff'):
349 with ui.timeblockedsection(b'extdiff'):
350 for proc in waitprocs:
350 for proc in waitprocs:
351 proc.wait()
351 proc.wait()
352
352
353
353
354 def dodiff(ui, repo, cmdline, pats, opts, guitool=False):
354 def dodiff(ui, repo, cmdline, pats, opts, guitool=False):
355 '''Do the actual diff:
355 '''Do the actual diff:
356
356
357 - copy to a temp structure if diffing 2 internal revisions
357 - copy to a temp structure if diffing 2 internal revisions
358 - copy to a temp structure if diffing working revision with
358 - copy to a temp structure if diffing working revision with
359 another one and more than 1 file is changed
359 another one and more than 1 file is changed
360 - just invoke the diff for a single file in the working dir
360 - just invoke the diff for a single file in the working dir
361 '''
361 '''
362
362
363 cmdutil.check_at_most_one_arg(opts, b'rev', b'change')
363 revs = opts.get(b'rev')
364 revs = opts.get(b'rev')
364 change = opts.get(b'change')
365 change = opts.get(b'change')
365 do3way = b'$parent2' in cmdline
366 do3way = b'$parent2' in cmdline
366
367
367 if revs and change:
368 if change:
368 msg = _(b'cannot specify --rev and --change at the same time')
369 raise error.Abort(msg)
370 elif change:
371 ctx2 = scmutil.revsingle(repo, change, None)
369 ctx2 = scmutil.revsingle(repo, change, None)
372 ctx1a, ctx1b = ctx2.p1(), ctx2.p2()
370 ctx1a, ctx1b = ctx2.p1(), ctx2.p2()
373 else:
371 else:
374 ctx1a, ctx2 = scmutil.revpair(repo, revs)
372 ctx1a, ctx2 = scmutil.revpair(repo, revs)
375 if not revs:
373 if not revs:
376 ctx1b = repo[None].p2()
374 ctx1b = repo[None].p2()
377 else:
375 else:
378 ctx1b = repo[nullid]
376 ctx1b = repo[nullid]
379
377
380 perfile = opts.get(b'per_file')
378 perfile = opts.get(b'per_file')
381 confirm = opts.get(b'confirm')
379 confirm = opts.get(b'confirm')
382
380
383 node1a = ctx1a.node()
381 node1a = ctx1a.node()
384 node1b = ctx1b.node()
382 node1b = ctx1b.node()
385 node2 = ctx2.node()
383 node2 = ctx2.node()
386
384
387 # Disable 3-way merge if there is only one parent
385 # Disable 3-way merge if there is only one parent
388 if do3way:
386 if do3way:
389 if node1b == nullid:
387 if node1b == nullid:
390 do3way = False
388 do3way = False
391
389
392 subrepos = opts.get(b'subrepos')
390 subrepos = opts.get(b'subrepos')
393
391
394 matcher = scmutil.match(repo[node2], pats, opts)
392 matcher = scmutil.match(repo[node2], pats, opts)
395
393
396 if opts.get(b'patch'):
394 if opts.get(b'patch'):
397 if subrepos:
395 if subrepos:
398 raise error.Abort(_(b'--patch cannot be used with --subrepos'))
396 raise error.Abort(_(b'--patch cannot be used with --subrepos'))
399 if perfile:
397 if perfile:
400 raise error.Abort(_(b'--patch cannot be used with --per-file'))
398 raise error.Abort(_(b'--patch cannot be used with --per-file'))
401 if node2 is None:
399 if node2 is None:
402 raise error.Abort(_(b'--patch requires two revisions'))
400 raise error.Abort(_(b'--patch requires two revisions'))
403 else:
401 else:
404 st = repo.status(node1a, node2, matcher, listsubrepos=subrepos)
402 st = repo.status(node1a, node2, matcher, listsubrepos=subrepos)
405 mod_a, add_a, rem_a = set(st.modified), set(st.added), set(st.removed)
403 mod_a, add_a, rem_a = set(st.modified), set(st.added), set(st.removed)
406 if do3way:
404 if do3way:
407 stb = repo.status(node1b, node2, matcher, listsubrepos=subrepos)
405 stb = repo.status(node1b, node2, matcher, listsubrepos=subrepos)
408 mod_b, add_b, rem_b = (
406 mod_b, add_b, rem_b = (
409 set(stb.modified),
407 set(stb.modified),
410 set(stb.added),
408 set(stb.added),
411 set(stb.removed),
409 set(stb.removed),
412 )
410 )
413 else:
411 else:
414 mod_b, add_b, rem_b = set(), set(), set()
412 mod_b, add_b, rem_b = set(), set(), set()
415 modadd = mod_a | add_a | mod_b | add_b
413 modadd = mod_a | add_a | mod_b | add_b
416 common = modadd | rem_a | rem_b
414 common = modadd | rem_a | rem_b
417 if not common:
415 if not common:
418 return 0
416 return 0
419
417
420 tmproot = pycompat.mkdtemp(prefix=b'extdiff.')
418 tmproot = pycompat.mkdtemp(prefix=b'extdiff.')
421 try:
419 try:
422 if not opts.get(b'patch'):
420 if not opts.get(b'patch'):
423 # Always make a copy of node1a (and node1b, if applicable)
421 # Always make a copy of node1a (and node1b, if applicable)
424 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
422 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
425 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[
423 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[
426 0
424 0
427 ]
425 ]
428 rev1a = b'@%d' % repo[node1a].rev()
426 rev1a = b'@%d' % repo[node1a].rev()
429 if do3way:
427 if do3way:
430 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
428 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
431 dir1b = snapshot(
429 dir1b = snapshot(
432 ui, repo, dir1b_files, node1b, tmproot, subrepos
430 ui, repo, dir1b_files, node1b, tmproot, subrepos
433 )[0]
431 )[0]
434 rev1b = b'@%d' % repo[node1b].rev()
432 rev1b = b'@%d' % repo[node1b].rev()
435 else:
433 else:
436 dir1b = None
434 dir1b = None
437 rev1b = b''
435 rev1b = b''
438
436
439 fnsandstat = []
437 fnsandstat = []
440
438
441 # If node2 in not the wc or there is >1 change, copy it
439 # If node2 in not the wc or there is >1 change, copy it
442 dir2root = b''
440 dir2root = b''
443 rev2 = b''
441 rev2 = b''
444 if node2:
442 if node2:
445 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
443 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
446 rev2 = b'@%d' % repo[node2].rev()
444 rev2 = b'@%d' % repo[node2].rev()
447 elif len(common) > 1:
445 elif len(common) > 1:
448 # we only actually need to get the files to copy back to
446 # we only actually need to get the files to copy back to
449 # the working dir in this case (because the other cases
447 # the working dir in this case (because the other cases
450 # are: diffing 2 revisions or single file -- in which case
448 # are: diffing 2 revisions or single file -- in which case
451 # the file is already directly passed to the diff tool).
449 # the file is already directly passed to the diff tool).
452 dir2, fnsandstat = snapshot(
450 dir2, fnsandstat = snapshot(
453 ui, repo, modadd, None, tmproot, subrepos
451 ui, repo, modadd, None, tmproot, subrepos
454 )
452 )
455 else:
453 else:
456 # This lets the diff tool open the changed file directly
454 # This lets the diff tool open the changed file directly
457 dir2 = b''
455 dir2 = b''
458 dir2root = repo.root
456 dir2root = repo.root
459
457
460 label1a = rev1a
458 label1a = rev1a
461 label1b = rev1b
459 label1b = rev1b
462 label2 = rev2
460 label2 = rev2
463
461
464 # If only one change, diff the files instead of the directories
462 # If only one change, diff the files instead of the directories
465 # Handle bogus modifies correctly by checking if the files exist
463 # Handle bogus modifies correctly by checking if the files exist
466 if len(common) == 1:
464 if len(common) == 1:
467 common_file = util.localpath(common.pop())
465 common_file = util.localpath(common.pop())
468 dir1a = os.path.join(tmproot, dir1a, common_file)
466 dir1a = os.path.join(tmproot, dir1a, common_file)
469 label1a = common_file + rev1a
467 label1a = common_file + rev1a
470 if not os.path.isfile(dir1a):
468 if not os.path.isfile(dir1a):
471 dir1a = pycompat.osdevnull
469 dir1a = pycompat.osdevnull
472 if do3way:
470 if do3way:
473 dir1b = os.path.join(tmproot, dir1b, common_file)
471 dir1b = os.path.join(tmproot, dir1b, common_file)
474 label1b = common_file + rev1b
472 label1b = common_file + rev1b
475 if not os.path.isfile(dir1b):
473 if not os.path.isfile(dir1b):
476 dir1b = pycompat.osdevnull
474 dir1b = pycompat.osdevnull
477 dir2 = os.path.join(dir2root, dir2, common_file)
475 dir2 = os.path.join(dir2root, dir2, common_file)
478 label2 = common_file + rev2
476 label2 = common_file + rev2
479 else:
477 else:
480 template = b'hg-%h.patch'
478 template = b'hg-%h.patch'
481 with formatter.nullformatter(ui, b'extdiff', {}) as fm:
479 with formatter.nullformatter(ui, b'extdiff', {}) as fm:
482 cmdutil.export(
480 cmdutil.export(
483 repo,
481 repo,
484 [repo[node1a].rev(), repo[node2].rev()],
482 [repo[node1a].rev(), repo[node2].rev()],
485 fm,
483 fm,
486 fntemplate=repo.vfs.reljoin(tmproot, template),
484 fntemplate=repo.vfs.reljoin(tmproot, template),
487 match=matcher,
485 match=matcher,
488 )
486 )
489 label1a = cmdutil.makefilename(repo[node1a], template)
487 label1a = cmdutil.makefilename(repo[node1a], template)
490 label2 = cmdutil.makefilename(repo[node2], template)
488 label2 = cmdutil.makefilename(repo[node2], template)
491 dir1a = repo.vfs.reljoin(tmproot, label1a)
489 dir1a = repo.vfs.reljoin(tmproot, label1a)
492 dir2 = repo.vfs.reljoin(tmproot, label2)
490 dir2 = repo.vfs.reljoin(tmproot, label2)
493 dir1b = None
491 dir1b = None
494 label1b = None
492 label1b = None
495 fnsandstat = []
493 fnsandstat = []
496
494
497 if not perfile:
495 if not perfile:
498 # Run the external tool on the 2 temp directories or the patches
496 # Run the external tool on the 2 temp directories or the patches
499 cmdline = formatcmdline(
497 cmdline = formatcmdline(
500 cmdline,
498 cmdline,
501 repo.root,
499 repo.root,
502 do3way=do3way,
500 do3way=do3way,
503 parent1=dir1a,
501 parent1=dir1a,
504 plabel1=label1a,
502 plabel1=label1a,
505 parent2=dir1b,
503 parent2=dir1b,
506 plabel2=label1b,
504 plabel2=label1b,
507 child=dir2,
505 child=dir2,
508 clabel=label2,
506 clabel=label2,
509 )
507 )
510 ui.debug(
508 ui.debug(
511 b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot)
509 b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot)
512 )
510 )
513 ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
511 ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
514 else:
512 else:
515 # Run the external tool once for each pair of files
513 # Run the external tool once for each pair of files
516 _runperfilediff(
514 _runperfilediff(
517 cmdline,
515 cmdline,
518 repo.root,
516 repo.root,
519 ui,
517 ui,
520 guitool=guitool,
518 guitool=guitool,
521 do3way=do3way,
519 do3way=do3way,
522 confirm=confirm,
520 confirm=confirm,
523 commonfiles=common,
521 commonfiles=common,
524 tmproot=tmproot,
522 tmproot=tmproot,
525 dir1a=dir1a,
523 dir1a=dir1a,
526 dir1b=dir1b,
524 dir1b=dir1b,
527 dir2root=dir2root,
525 dir2root=dir2root,
528 dir2=dir2,
526 dir2=dir2,
529 rev1a=rev1a,
527 rev1a=rev1a,
530 rev1b=rev1b,
528 rev1b=rev1b,
531 rev2=rev2,
529 rev2=rev2,
532 )
530 )
533
531
534 for copy_fn, working_fn, st in fnsandstat:
532 for copy_fn, working_fn, st in fnsandstat:
535 cpstat = os.lstat(copy_fn)
533 cpstat = os.lstat(copy_fn)
536 # Some tools copy the file and attributes, so mtime may not detect
534 # Some tools copy the file and attributes, so mtime may not detect
537 # all changes. A size check will detect more cases, but not all.
535 # all changes. A size check will detect more cases, but not all.
538 # The only certain way to detect every case is to diff all files,
536 # The only certain way to detect every case is to diff all files,
539 # which could be expensive.
537 # which could be expensive.
540 # copyfile() carries over the permission, so the mode check could
538 # copyfile() carries over the permission, so the mode check could
541 # be in an 'elif' branch, but for the case where the file has
539 # be in an 'elif' branch, but for the case where the file has
542 # changed without affecting mtime or size.
540 # changed without affecting mtime or size.
543 if (
541 if (
544 cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
542 cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
545 or cpstat.st_size != st.st_size
543 or cpstat.st_size != st.st_size
546 or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)
544 or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)
547 ):
545 ):
548 ui.debug(
546 ui.debug(
549 b'file changed while diffing. '
547 b'file changed while diffing. '
550 b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)
548 b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)
551 )
549 )
552 util.copyfile(copy_fn, working_fn)
550 util.copyfile(copy_fn, working_fn)
553
551
554 return 1
552 return 1
555 finally:
553 finally:
556 ui.note(_(b'cleaning up temp directory\n'))
554 ui.note(_(b'cleaning up temp directory\n'))
557 shutil.rmtree(tmproot)
555 shutil.rmtree(tmproot)
558
556
559
557
560 extdiffopts = (
558 extdiffopts = (
561 [
559 [
562 (
560 (
563 b'o',
561 b'o',
564 b'option',
562 b'option',
565 [],
563 [],
566 _(b'pass option to comparison program'),
564 _(b'pass option to comparison program'),
567 _(b'OPT'),
565 _(b'OPT'),
568 ),
566 ),
569 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
567 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
570 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
568 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
571 (
569 (
572 b'',
570 b'',
573 b'per-file',
571 b'per-file',
574 False,
572 False,
575 _(b'compare each file instead of revision snapshots'),
573 _(b'compare each file instead of revision snapshots'),
576 ),
574 ),
577 (
575 (
578 b'',
576 b'',
579 b'confirm',
577 b'confirm',
580 False,
578 False,
581 _(b'prompt user before each external program invocation'),
579 _(b'prompt user before each external program invocation'),
582 ),
580 ),
583 (b'', b'patch', None, _(b'compare patches for two revisions')),
581 (b'', b'patch', None, _(b'compare patches for two revisions')),
584 ]
582 ]
585 + cmdutil.walkopts
583 + cmdutil.walkopts
586 + cmdutil.subrepoopts
584 + cmdutil.subrepoopts
587 )
585 )
588
586
589
587
590 @command(
588 @command(
591 b'extdiff',
589 b'extdiff',
592 [(b'p', b'program', b'', _(b'comparison program to run'), _(b'CMD')),]
590 [(b'p', b'program', b'', _(b'comparison program to run'), _(b'CMD')),]
593 + extdiffopts,
591 + extdiffopts,
594 _(b'hg extdiff [OPT]... [FILE]...'),
592 _(b'hg extdiff [OPT]... [FILE]...'),
595 helpcategory=command.CATEGORY_FILE_CONTENTS,
593 helpcategory=command.CATEGORY_FILE_CONTENTS,
596 inferrepo=True,
594 inferrepo=True,
597 )
595 )
598 def extdiff(ui, repo, *pats, **opts):
596 def extdiff(ui, repo, *pats, **opts):
599 '''use external program to diff repository (or selected files)
597 '''use external program to diff repository (or selected files)
600
598
601 Show differences between revisions for the specified files, using
599 Show differences between revisions for the specified files, using
602 an external program. The default program used is diff, with
600 an external program. The default program used is diff, with
603 default options "-Npru".
601 default options "-Npru".
604
602
605 To select a different program, use the -p/--program option. The
603 To select a different program, use the -p/--program option. The
606 program will be passed the names of two directories to compare,
604 program will be passed the names of two directories to compare,
607 unless the --per-file option is specified (see below). To pass
605 unless the --per-file option is specified (see below). To pass
608 additional options to the program, use -o/--option. These will be
606 additional options to the program, use -o/--option. These will be
609 passed before the names of the directories or files to compare.
607 passed before the names of the directories or files to compare.
610
608
611 When two revision arguments are given, then changes are shown
609 When two revision arguments are given, then changes are shown
612 between those revisions. If only one revision is specified then
610 between those revisions. If only one revision is specified then
613 that revision is compared to the working directory, and, when no
611 that revision is compared to the working directory, and, when no
614 revisions are specified, the working directory files are compared
612 revisions are specified, the working directory files are compared
615 to its parent.
613 to its parent.
616
614
617 The --per-file option runs the external program repeatedly on each
615 The --per-file option runs the external program repeatedly on each
618 file to diff, instead of once on two directories. By default,
616 file to diff, instead of once on two directories. By default,
619 this happens one by one, where the next file diff is open in the
617 this happens one by one, where the next file diff is open in the
620 external program only once the previous external program (for the
618 external program only once the previous external program (for the
621 previous file diff) has exited. If the external program has a
619 previous file diff) has exited. If the external program has a
622 graphical interface, it can open all the file diffs at once instead
620 graphical interface, it can open all the file diffs at once instead
623 of one by one. See :hg:`help -e extdiff` for information about how
621 of one by one. See :hg:`help -e extdiff` for information about how
624 to tell Mercurial that a given program has a graphical interface.
622 to tell Mercurial that a given program has a graphical interface.
625
623
626 The --confirm option will prompt the user before each invocation of
624 The --confirm option will prompt the user before each invocation of
627 the external program. It is ignored if --per-file isn't specified.
625 the external program. It is ignored if --per-file isn't specified.
628 '''
626 '''
629 opts = pycompat.byteskwargs(opts)
627 opts = pycompat.byteskwargs(opts)
630 program = opts.get(b'program')
628 program = opts.get(b'program')
631 option = opts.get(b'option')
629 option = opts.get(b'option')
632 if not program:
630 if not program:
633 program = b'diff'
631 program = b'diff'
634 option = option or [b'-Npru']
632 option = option or [b'-Npru']
635 cmdline = b' '.join(map(procutil.shellquote, [program] + option))
633 cmdline = b' '.join(map(procutil.shellquote, [program] + option))
636 return dodiff(ui, repo, cmdline, pats, opts)
634 return dodiff(ui, repo, cmdline, pats, opts)
637
635
638
636
639 class savedcmd(object):
637 class savedcmd(object):
640 """use external program to diff repository (or selected files)
638 """use external program to diff repository (or selected files)
641
639
642 Show differences between revisions for the specified files, using
640 Show differences between revisions for the specified files, using
643 the following program::
641 the following program::
644
642
645 %(path)s
643 %(path)s
646
644
647 When two revision arguments are given, then changes are shown
645 When two revision arguments are given, then changes are shown
648 between those revisions. If only one revision is specified then
646 between those revisions. If only one revision is specified then
649 that revision is compared to the working directory, and, when no
647 that revision is compared to the working directory, and, when no
650 revisions are specified, the working directory files are compared
648 revisions are specified, the working directory files are compared
651 to its parent.
649 to its parent.
652 """
650 """
653
651
654 def __init__(self, path, cmdline, isgui):
652 def __init__(self, path, cmdline, isgui):
655 # We can't pass non-ASCII through docstrings (and path is
653 # We can't pass non-ASCII through docstrings (and path is
656 # in an unknown encoding anyway), but avoid double separators on
654 # in an unknown encoding anyway), but avoid double separators on
657 # Windows
655 # Windows
658 docpath = stringutil.escapestr(path).replace(b'\\\\', b'\\')
656 docpath = stringutil.escapestr(path).replace(b'\\\\', b'\\')
659 self.__doc__ %= {'path': pycompat.sysstr(stringutil.uirepr(docpath))}
657 self.__doc__ %= {'path': pycompat.sysstr(stringutil.uirepr(docpath))}
660 self._cmdline = cmdline
658 self._cmdline = cmdline
661 self._isgui = isgui
659 self._isgui = isgui
662
660
663 def __call__(self, ui, repo, *pats, **opts):
661 def __call__(self, ui, repo, *pats, **opts):
664 opts = pycompat.byteskwargs(opts)
662 opts = pycompat.byteskwargs(opts)
665 options = b' '.join(map(procutil.shellquote, opts[b'option']))
663 options = b' '.join(map(procutil.shellquote, opts[b'option']))
666 if options:
664 if options:
667 options = b' ' + options
665 options = b' ' + options
668 return dodiff(
666 return dodiff(
669 ui, repo, self._cmdline + options, pats, opts, guitool=self._isgui
667 ui, repo, self._cmdline + options, pats, opts, guitool=self._isgui
670 )
668 )
671
669
672
670
673 def uisetup(ui):
671 def uisetup(ui):
674 for cmd, path in ui.configitems(b'extdiff'):
672 for cmd, path in ui.configitems(b'extdiff'):
675 path = util.expandpath(path)
673 path = util.expandpath(path)
676 if cmd.startswith(b'cmd.'):
674 if cmd.startswith(b'cmd.'):
677 cmd = cmd[4:]
675 cmd = cmd[4:]
678 if not path:
676 if not path:
679 path = procutil.findexe(cmd)
677 path = procutil.findexe(cmd)
680 if path is None:
678 if path is None:
681 path = filemerge.findexternaltool(ui, cmd) or cmd
679 path = filemerge.findexternaltool(ui, cmd) or cmd
682 diffopts = ui.config(b'extdiff', b'opts.' + cmd)
680 diffopts = ui.config(b'extdiff', b'opts.' + cmd)
683 cmdline = procutil.shellquote(path)
681 cmdline = procutil.shellquote(path)
684 if diffopts:
682 if diffopts:
685 cmdline += b' ' + diffopts
683 cmdline += b' ' + diffopts
686 isgui = ui.configbool(b'extdiff', b'gui.' + cmd)
684 isgui = ui.configbool(b'extdiff', b'gui.' + cmd)
687 elif cmd.startswith(b'opts.') or cmd.startswith(b'gui.'):
685 elif cmd.startswith(b'opts.') or cmd.startswith(b'gui.'):
688 continue
686 continue
689 else:
687 else:
690 if path:
688 if path:
691 # case "cmd = path opts"
689 # case "cmd = path opts"
692 cmdline = path
690 cmdline = path
693 diffopts = len(pycompat.shlexsplit(cmdline)) > 1
691 diffopts = len(pycompat.shlexsplit(cmdline)) > 1
694 else:
692 else:
695 # case "cmd ="
693 # case "cmd ="
696 path = procutil.findexe(cmd)
694 path = procutil.findexe(cmd)
697 if path is None:
695 if path is None:
698 path = filemerge.findexternaltool(ui, cmd) or cmd
696 path = filemerge.findexternaltool(ui, cmd) or cmd
699 cmdline = procutil.shellquote(path)
697 cmdline = procutil.shellquote(path)
700 diffopts = False
698 diffopts = False
701 isgui = ui.configbool(b'extdiff', b'gui.' + cmd)
699 isgui = ui.configbool(b'extdiff', b'gui.' + cmd)
702 # look for diff arguments in [diff-tools] then [merge-tools]
700 # look for diff arguments in [diff-tools] then [merge-tools]
703 if not diffopts:
701 if not diffopts:
704 key = cmd + b'.diffargs'
702 key = cmd + b'.diffargs'
705 for section in (b'diff-tools', b'merge-tools'):
703 for section in (b'diff-tools', b'merge-tools'):
706 args = ui.config(section, key)
704 args = ui.config(section, key)
707 if args:
705 if args:
708 cmdline += b' ' + args
706 cmdline += b' ' + args
709 if isgui is None:
707 if isgui is None:
710 isgui = ui.configbool(section, cmd + b'.gui') or False
708 isgui = ui.configbool(section, cmd + b'.gui') or False
711 break
709 break
712 command(
710 command(
713 cmd,
711 cmd,
714 extdiffopts[:],
712 extdiffopts[:],
715 _(b'hg %s [OPTION]... [FILE]...') % cmd,
713 _(b'hg %s [OPTION]... [FILE]...') % cmd,
716 helpcategory=command.CATEGORY_FILE_CONTENTS,
714 helpcategory=command.CATEGORY_FILE_CONTENTS,
717 inferrepo=True,
715 inferrepo=True,
718 )(savedcmd(path, cmdline, isgui))
716 )(savedcmd(path, cmdline, isgui))
719
717
720
718
721 # tell hggettext to extract docstrings from these functions:
719 # tell hggettext to extract docstrings from these functions:
722 i18nfunctions = [savedcmd]
720 i18nfunctions = [savedcmd]
@@ -1,7837 +1,7835 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from .pycompat import open
25 from .pycompat import open
26 from . import (
26 from . import (
27 archival,
27 archival,
28 bookmarks,
28 bookmarks,
29 bundle2,
29 bundle2,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 merge as mergemod,
48 merge as mergemod,
49 narrowspec,
49 narrowspec,
50 obsolete,
50 obsolete,
51 obsutil,
51 obsutil,
52 patch,
52 patch,
53 phases,
53 phases,
54 pycompat,
54 pycompat,
55 rcutil,
55 rcutil,
56 registrar,
56 registrar,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 shelve as shelvemod,
61 shelve as shelvemod,
62 state as statemod,
62 state as statemod,
63 streamclone,
63 streamclone,
64 tags as tagsmod,
64 tags as tagsmod,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 verify as verifymod,
67 verify as verifymod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 )
73 )
74
74
75 table = {}
75 table = {}
76 table.update(debugcommandsmod.command._table)
76 table.update(debugcommandsmod.command._table)
77
77
78 command = registrar.command(table)
78 command = registrar.command(table)
79 INTENT_READONLY = registrar.INTENT_READONLY
79 INTENT_READONLY = registrar.INTENT_READONLY
80
80
81 # common command options
81 # common command options
82
82
83 globalopts = [
83 globalopts = [
84 (
84 (
85 b'R',
85 b'R',
86 b'repository',
86 b'repository',
87 b'',
87 b'',
88 _(b'repository root directory or name of overlay bundle file'),
88 _(b'repository root directory or name of overlay bundle file'),
89 _(b'REPO'),
89 _(b'REPO'),
90 ),
90 ),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (
92 (
93 b'y',
93 b'y',
94 b'noninteractive',
94 b'noninteractive',
95 None,
95 None,
96 _(
96 _(
97 b'do not prompt, automatically pick the first choice for all prompts'
97 b'do not prompt, automatically pick the first choice for all prompts'
98 ),
98 ),
99 ),
99 ),
100 (b'q', b'quiet', None, _(b'suppress output')),
100 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
102 (
102 (
103 b'',
103 b'',
104 b'color',
104 b'color',
105 b'',
105 b'',
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # and should not be translated
107 # and should not be translated
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b'TYPE'),
109 _(b'TYPE'),
110 ),
110 ),
111 (
111 (
112 b'',
112 b'',
113 b'config',
113 b'config',
114 [],
114 [],
115 _(b'set/override config option (use \'section.name=value\')'),
115 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'CONFIG'),
116 _(b'CONFIG'),
117 ),
117 ),
118 (b'', b'debug', None, _(b'enable debugging output')),
118 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debugger', None, _(b'start debugger')),
119 (b'', b'debugger', None, _(b'start debugger')),
120 (
120 (
121 b'',
121 b'',
122 b'encoding',
122 b'encoding',
123 encoding.encoding,
123 encoding.encoding,
124 _(b'set the charset encoding'),
124 _(b'set the charset encoding'),
125 _(b'ENCODE'),
125 _(b'ENCODE'),
126 ),
126 ),
127 (
127 (
128 b'',
128 b'',
129 b'encodingmode',
129 b'encodingmode',
130 encoding.encodingmode,
130 encoding.encodingmode,
131 _(b'set the charset encoding mode'),
131 _(b'set the charset encoding mode'),
132 _(b'MODE'),
132 _(b'MODE'),
133 ),
133 ),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'time', None, _(b'time how long the command takes')),
135 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'profile', None, _(b'print command execution profile')),
136 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'version', None, _(b'output version information and exit')),
137 (b'', b'version', None, _(b'output version information and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (
140 (
141 b'',
141 b'',
142 b'pager',
142 b'pager',
143 b'auto',
143 b'auto',
144 _(b"when to paginate (boolean, always, auto, or never)"),
144 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b'TYPE'),
145 _(b'TYPE'),
146 ),
146 ),
147 ]
147 ]
148
148
149 dryrunopts = cmdutil.dryrunopts
149 dryrunopts = cmdutil.dryrunopts
150 remoteopts = cmdutil.remoteopts
150 remoteopts = cmdutil.remoteopts
151 walkopts = cmdutil.walkopts
151 walkopts = cmdutil.walkopts
152 commitopts = cmdutil.commitopts
152 commitopts = cmdutil.commitopts
153 commitopts2 = cmdutil.commitopts2
153 commitopts2 = cmdutil.commitopts2
154 commitopts3 = cmdutil.commitopts3
154 commitopts3 = cmdutil.commitopts3
155 formatteropts = cmdutil.formatteropts
155 formatteropts = cmdutil.formatteropts
156 templateopts = cmdutil.templateopts
156 templateopts = cmdutil.templateopts
157 logopts = cmdutil.logopts
157 logopts = cmdutil.logopts
158 diffopts = cmdutil.diffopts
158 diffopts = cmdutil.diffopts
159 diffwsopts = cmdutil.diffwsopts
159 diffwsopts = cmdutil.diffwsopts
160 diffopts2 = cmdutil.diffopts2
160 diffopts2 = cmdutil.diffopts2
161 mergetoolopts = cmdutil.mergetoolopts
161 mergetoolopts = cmdutil.mergetoolopts
162 similarityopts = cmdutil.similarityopts
162 similarityopts = cmdutil.similarityopts
163 subrepoopts = cmdutil.subrepoopts
163 subrepoopts = cmdutil.subrepoopts
164 debugrevlogopts = cmdutil.debugrevlogopts
164 debugrevlogopts = cmdutil.debugrevlogopts
165
165
166 # Commands start here, listed alphabetically
166 # Commands start here, listed alphabetically
167
167
168
168
169 @command(
169 @command(
170 b'abort',
170 b'abort',
171 dryrunopts,
171 dryrunopts,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpbasic=True,
173 helpbasic=True,
174 )
174 )
175 def abort(ui, repo, **opts):
175 def abort(ui, repo, **opts):
176 """abort an unfinished operation (EXPERIMENTAL)
176 """abort an unfinished operation (EXPERIMENTAL)
177
177
178 Aborts a multistep operation like graft, histedit, rebase, merge,
178 Aborts a multistep operation like graft, histedit, rebase, merge,
179 and unshelve if they are in an unfinished state.
179 and unshelve if they are in an unfinished state.
180
180
181 use --dry-run/-n to dry run the command.
181 use --dry-run/-n to dry run the command.
182 """
182 """
183 dryrun = opts.get('dry_run')
183 dryrun = opts.get('dry_run')
184 abortstate = cmdutil.getunfinishedstate(repo)
184 abortstate = cmdutil.getunfinishedstate(repo)
185 if not abortstate:
185 if not abortstate:
186 raise error.Abort(_(b'no operation in progress'))
186 raise error.Abort(_(b'no operation in progress'))
187 if not abortstate.abortfunc:
187 if not abortstate.abortfunc:
188 raise error.Abort(
188 raise error.Abort(
189 (
189 (
190 _(b"%s in progress but does not support 'hg abort'")
190 _(b"%s in progress but does not support 'hg abort'")
191 % (abortstate._opname)
191 % (abortstate._opname)
192 ),
192 ),
193 hint=abortstate.hint(),
193 hint=abortstate.hint(),
194 )
194 )
195 if dryrun:
195 if dryrun:
196 ui.status(
196 ui.status(
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 )
198 )
199 return
199 return
200 return abortstate.abortfunc(ui, repo)
200 return abortstate.abortfunc(ui, repo)
201
201
202
202
203 @command(
203 @command(
204 b'add',
204 b'add',
205 walkopts + subrepoopts + dryrunopts,
205 walkopts + subrepoopts + dryrunopts,
206 _(b'[OPTION]... [FILE]...'),
206 _(b'[OPTION]... [FILE]...'),
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpbasic=True,
208 helpbasic=True,
209 inferrepo=True,
209 inferrepo=True,
210 )
210 )
211 def add(ui, repo, *pats, **opts):
211 def add(ui, repo, *pats, **opts):
212 """add the specified files on the next commit
212 """add the specified files on the next commit
213
213
214 Schedule files to be version controlled and added to the
214 Schedule files to be version controlled and added to the
215 repository.
215 repository.
216
216
217 The files will be added to the repository at the next commit. To
217 The files will be added to the repository at the next commit. To
218 undo an add before that, see :hg:`forget`.
218 undo an add before that, see :hg:`forget`.
219
219
220 If no names are given, add all files to the repository (except
220 If no names are given, add all files to the repository (except
221 files matching ``.hgignore``).
221 files matching ``.hgignore``).
222
222
223 .. container:: verbose
223 .. container:: verbose
224
224
225 Examples:
225 Examples:
226
226
227 - New (unknown) files are added
227 - New (unknown) files are added
228 automatically by :hg:`add`::
228 automatically by :hg:`add`::
229
229
230 $ ls
230 $ ls
231 foo.c
231 foo.c
232 $ hg status
232 $ hg status
233 ? foo.c
233 ? foo.c
234 $ hg add
234 $ hg add
235 adding foo.c
235 adding foo.c
236 $ hg status
236 $ hg status
237 A foo.c
237 A foo.c
238
238
239 - Specific files to be added can be specified::
239 - Specific files to be added can be specified::
240
240
241 $ ls
241 $ ls
242 bar.c foo.c
242 bar.c foo.c
243 $ hg status
243 $ hg status
244 ? bar.c
244 ? bar.c
245 ? foo.c
245 ? foo.c
246 $ hg add bar.c
246 $ hg add bar.c
247 $ hg status
247 $ hg status
248 A bar.c
248 A bar.c
249 ? foo.c
249 ? foo.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253
253
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 return rejected and 1 or 0
257 return rejected and 1 or 0
258
258
259
259
260 @command(
260 @command(
261 b'addremove',
261 b'addremove',
262 similarityopts + subrepoopts + walkopts + dryrunopts,
262 similarityopts + subrepoopts + walkopts + dryrunopts,
263 _(b'[OPTION]... [FILE]...'),
263 _(b'[OPTION]... [FILE]...'),
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 inferrepo=True,
265 inferrepo=True,
266 )
266 )
267 def addremove(ui, repo, *pats, **opts):
267 def addremove(ui, repo, *pats, **opts):
268 """add all new files, delete all missing files
268 """add all new files, delete all missing files
269
269
270 Add all new files and remove all missing files from the
270 Add all new files and remove all missing files from the
271 repository.
271 repository.
272
272
273 Unless names are given, new files are ignored if they match any of
273 Unless names are given, new files are ignored if they match any of
274 the patterns in ``.hgignore``. As with add, these changes take
274 the patterns in ``.hgignore``. As with add, these changes take
275 effect at the next commit.
275 effect at the next commit.
276
276
277 Use the -s/--similarity option to detect renamed files. This
277 Use the -s/--similarity option to detect renamed files. This
278 option takes a percentage between 0 (disabled) and 100 (files must
278 option takes a percentage between 0 (disabled) and 100 (files must
279 be identical) as its parameter. With a parameter greater than 0,
279 be identical) as its parameter. With a parameter greater than 0,
280 this compares every removed file with every added file and records
280 this compares every removed file with every added file and records
281 those similar enough as renames. Detecting renamed files this way
281 those similar enough as renames. Detecting renamed files this way
282 can be expensive. After using this option, :hg:`status -C` can be
282 can be expensive. After using this option, :hg:`status -C` can be
283 used to check which files were identified as moved or renamed. If
283 used to check which files were identified as moved or renamed. If
284 not specified, -s/--similarity defaults to 100 and only renames of
284 not specified, -s/--similarity defaults to 100 and only renames of
285 identical files are detected.
285 identical files are detected.
286
286
287 .. container:: verbose
287 .. container:: verbose
288
288
289 Examples:
289 Examples:
290
290
291 - A number of files (bar.c and foo.c) are new,
291 - A number of files (bar.c and foo.c) are new,
292 while foobar.c has been removed (without using :hg:`remove`)
292 while foobar.c has been removed (without using :hg:`remove`)
293 from the repository::
293 from the repository::
294
294
295 $ ls
295 $ ls
296 bar.c foo.c
296 bar.c foo.c
297 $ hg status
297 $ hg status
298 ! foobar.c
298 ! foobar.c
299 ? bar.c
299 ? bar.c
300 ? foo.c
300 ? foo.c
301 $ hg addremove
301 $ hg addremove
302 adding bar.c
302 adding bar.c
303 adding foo.c
303 adding foo.c
304 removing foobar.c
304 removing foobar.c
305 $ hg status
305 $ hg status
306 A bar.c
306 A bar.c
307 A foo.c
307 A foo.c
308 R foobar.c
308 R foobar.c
309
309
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 Afterwards, it was edited slightly::
311 Afterwards, it was edited slightly::
312
312
313 $ ls
313 $ ls
314 foo.c
314 foo.c
315 $ hg status
315 $ hg status
316 ! foobar.c
316 ! foobar.c
317 ? foo.c
317 ? foo.c
318 $ hg addremove --similarity 90
318 $ hg addremove --similarity 90
319 removing foobar.c
319 removing foobar.c
320 adding foo.c
320 adding foo.c
321 recording removal of foobar.c as rename to foo.c (94% similar)
321 recording removal of foobar.c as rename to foo.c (94% similar)
322 $ hg status -C
322 $ hg status -C
323 A foo.c
323 A foo.c
324 foobar.c
324 foobar.c
325 R foobar.c
325 R foobar.c
326
326
327 Returns 0 if all files are successfully added.
327 Returns 0 if all files are successfully added.
328 """
328 """
329 opts = pycompat.byteskwargs(opts)
329 opts = pycompat.byteskwargs(opts)
330 if not opts.get(b'similarity'):
330 if not opts.get(b'similarity'):
331 opts[b'similarity'] = b'100'
331 opts[b'similarity'] = b'100'
332 matcher = scmutil.match(repo[None], pats, opts)
332 matcher = scmutil.match(repo[None], pats, opts)
333 relative = scmutil.anypats(pats, opts)
333 relative = scmutil.anypats(pats, opts)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336
336
337
337
338 @command(
338 @command(
339 b'annotate|blame',
339 b'annotate|blame',
340 [
340 [
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (
342 (
343 b'',
343 b'',
344 b'follow',
344 b'follow',
345 None,
345 None,
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 ),
347 ),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'a', b'text', None, _(b'treat all files as text')),
349 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'f', b'file', None, _(b'list the filename')),
351 (b'f', b'file', None, _(b'list the filename')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
355 (
355 (
356 b'l',
356 b'l',
357 b'line-number',
357 b'line-number',
358 None,
358 None,
359 _(b'show line number at the first appearance'),
359 _(b'show line number at the first appearance'),
360 ),
360 ),
361 (
361 (
362 b'',
362 b'',
363 b'skip',
363 b'skip',
364 [],
364 [],
365 _(b'revset to not display (EXPERIMENTAL)'),
365 _(b'revset to not display (EXPERIMENTAL)'),
366 _(b'REV'),
366 _(b'REV'),
367 ),
367 ),
368 ]
368 ]
369 + diffwsopts
369 + diffwsopts
370 + walkopts
370 + walkopts
371 + formatteropts,
371 + formatteropts,
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpbasic=True,
374 helpbasic=True,
375 inferrepo=True,
375 inferrepo=True,
376 )
376 )
377 def annotate(ui, repo, *pats, **opts):
377 def annotate(ui, repo, *pats, **opts):
378 """show changeset information by line for each file
378 """show changeset information by line for each file
379
379
380 List changes in files, showing the revision id responsible for
380 List changes in files, showing the revision id responsible for
381 each line.
381 each line.
382
382
383 This command is useful for discovering when a change was made and
383 This command is useful for discovering when a change was made and
384 by whom.
384 by whom.
385
385
386 If you include --file, --user, or --date, the revision number is
386 If you include --file, --user, or --date, the revision number is
387 suppressed unless you also include --number.
387 suppressed unless you also include --number.
388
388
389 Without the -a/--text option, annotate will avoid processing files
389 Without the -a/--text option, annotate will avoid processing files
390 it detects as binary. With -a, annotate will annotate the file
390 it detects as binary. With -a, annotate will annotate the file
391 anyway, although the results will probably be neither useful
391 anyway, although the results will probably be neither useful
392 nor desirable.
392 nor desirable.
393
393
394 .. container:: verbose
394 .. container:: verbose
395
395
396 Template:
396 Template:
397
397
398 The following keywords are supported in addition to the common template
398 The following keywords are supported in addition to the common template
399 keywords and functions. See also :hg:`help templates`.
399 keywords and functions. See also :hg:`help templates`.
400
400
401 :lines: List of lines with annotation data.
401 :lines: List of lines with annotation data.
402 :path: String. Repository-absolute path of the specified file.
402 :path: String. Repository-absolute path of the specified file.
403
403
404 And each entry of ``{lines}`` provides the following sub-keywords in
404 And each entry of ``{lines}`` provides the following sub-keywords in
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406
406
407 :line: String. Line content.
407 :line: String. Line content.
408 :lineno: Integer. Line number at that revision.
408 :lineno: Integer. Line number at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
410
410
411 See :hg:`help templates.operators` for the list expansion syntax.
411 See :hg:`help templates.operators` for the list expansion syntax.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 """
414 """
415 opts = pycompat.byteskwargs(opts)
415 opts = pycompat.byteskwargs(opts)
416 if not pats:
416 if not pats:
417 raise error.Abort(_(b'at least one filename or pattern is required'))
417 raise error.Abort(_(b'at least one filename or pattern is required'))
418
418
419 if opts.get(b'follow'):
419 if opts.get(b'follow'):
420 # --follow is deprecated and now just an alias for -f/--file
420 # --follow is deprecated and now just an alias for -f/--file
421 # to mimic the behavior of Mercurial before version 1.5
421 # to mimic the behavior of Mercurial before version 1.5
422 opts[b'file'] = True
422 opts[b'file'] = True
423
423
424 if (
424 if (
425 not opts.get(b'user')
425 not opts.get(b'user')
426 and not opts.get(b'changeset')
426 and not opts.get(b'changeset')
427 and not opts.get(b'date')
427 and not opts.get(b'date')
428 and not opts.get(b'file')
428 and not opts.get(b'file')
429 ):
429 ):
430 opts[b'number'] = True
430 opts[b'number'] = True
431
431
432 linenumber = opts.get(b'line_number') is not None
432 linenumber = opts.get(b'line_number') is not None
433 if (
433 if (
434 linenumber
434 linenumber
435 and (not opts.get(b'changeset'))
435 and (not opts.get(b'changeset'))
436 and (not opts.get(b'number'))
436 and (not opts.get(b'number'))
437 ):
437 ):
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439
439
440 rev = opts.get(b'rev')
440 rev = opts.get(b'rev')
441 if rev:
441 if rev:
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443 ctx = scmutil.revsingle(repo, rev)
443 ctx = scmutil.revsingle(repo, rev)
444
444
445 ui.pager(b'annotate')
445 ui.pager(b'annotate')
446 rootfm = ui.formatter(b'annotate', opts)
446 rootfm = ui.formatter(b'annotate', opts)
447 if ui.debugflag:
447 if ui.debugflag:
448 shorthex = pycompat.identity
448 shorthex = pycompat.identity
449 else:
449 else:
450
450
451 def shorthex(h):
451 def shorthex(h):
452 return h[:12]
452 return h[:12]
453
453
454 if ui.quiet:
454 if ui.quiet:
455 datefunc = dateutil.shortdate
455 datefunc = dateutil.shortdate
456 else:
456 else:
457 datefunc = dateutil.datestr
457 datefunc = dateutil.datestr
458 if ctx.rev() is None:
458 if ctx.rev() is None:
459 if opts.get(b'changeset'):
459 if opts.get(b'changeset'):
460 # omit "+" suffix which is appended to node hex
460 # omit "+" suffix which is appended to node hex
461 def formatrev(rev):
461 def formatrev(rev):
462 if rev == wdirrev:
462 if rev == wdirrev:
463 return b'%d' % ctx.p1().rev()
463 return b'%d' % ctx.p1().rev()
464 else:
464 else:
465 return b'%d' % rev
465 return b'%d' % rev
466
466
467 else:
467 else:
468
468
469 def formatrev(rev):
469 def formatrev(rev):
470 if rev == wdirrev:
470 if rev == wdirrev:
471 return b'%d+' % ctx.p1().rev()
471 return b'%d+' % ctx.p1().rev()
472 else:
472 else:
473 return b'%d ' % rev
473 return b'%d ' % rev
474
474
475 def formathex(h):
475 def formathex(h):
476 if h == wdirhex:
476 if h == wdirhex:
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
478 else:
478 else:
479 return b'%s ' % shorthex(h)
479 return b'%s ' % shorthex(h)
480
480
481 else:
481 else:
482 formatrev = b'%d'.__mod__
482 formatrev = b'%d'.__mod__
483 formathex = shorthex
483 formathex = shorthex
484
484
485 opmap = [
485 opmap = [
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 ]
492 ]
493 opnamemap = {
493 opnamemap = {
494 b'rev': b'number',
494 b'rev': b'number',
495 b'node': b'changeset',
495 b'node': b'changeset',
496 b'path': b'file',
496 b'path': b'file',
497 b'lineno': b'line_number',
497 b'lineno': b'line_number',
498 }
498 }
499
499
500 if rootfm.isplain():
500 if rootfm.isplain():
501
501
502 def makefunc(get, fmt):
502 def makefunc(get, fmt):
503 return lambda x: fmt(get(x))
503 return lambda x: fmt(get(x))
504
504
505 else:
505 else:
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return get
508 return get
509
509
510 datahint = rootfm.datahint()
510 datahint = rootfm.datahint()
511 funcmap = [
511 funcmap = [
512 (makefunc(get, fmt), sep)
512 (makefunc(get, fmt), sep)
513 for fn, sep, get, fmt in opmap
513 for fn, sep, get, fmt in opmap
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
515 ]
515 ]
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
517 fields = b' '.join(
517 fields = b' '.join(
518 fn
518 fn
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 )
521 )
522
522
523 def bad(x, y):
523 def bad(x, y):
524 raise error.Abort(b"%s: %s" % (x, y))
524 raise error.Abort(b"%s: %s" % (x, y))
525
525
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
527
527
528 follow = not opts.get(b'no_follow')
528 follow = not opts.get(b'no_follow')
529 diffopts = patch.difffeatureopts(
529 diffopts = patch.difffeatureopts(
530 ui, opts, section=b'annotate', whitespace=True
530 ui, opts, section=b'annotate', whitespace=True
531 )
531 )
532 skiprevs = opts.get(b'skip')
532 skiprevs = opts.get(b'skip')
533 if skiprevs:
533 if skiprevs:
534 skiprevs = scmutil.revrange(repo, skiprevs)
534 skiprevs = scmutil.revrange(repo, skiprevs)
535
535
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
537 for abs in ctx.walk(m):
537 for abs in ctx.walk(m):
538 fctx = ctx[abs]
538 fctx = ctx[abs]
539 rootfm.startitem()
539 rootfm.startitem()
540 rootfm.data(path=abs)
540 rootfm.data(path=abs)
541 if not opts.get(b'text') and fctx.isbinary():
541 if not opts.get(b'text') and fctx.isbinary():
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 continue
543 continue
544
544
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 lines = fctx.annotate(
546 lines = fctx.annotate(
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
548 )
548 )
549 if not lines:
549 if not lines:
550 fm.end()
550 fm.end()
551 continue
551 continue
552 formats = []
552 formats = []
553 pieces = []
553 pieces = []
554
554
555 for f, sep in funcmap:
555 for f, sep in funcmap:
556 l = [f(n) for n in lines]
556 l = [f(n) for n in lines]
557 if fm.isplain():
557 if fm.isplain():
558 sizes = [encoding.colwidth(x) for x in l]
558 sizes = [encoding.colwidth(x) for x in l]
559 ml = max(sizes)
559 ml = max(sizes)
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561 else:
561 else:
562 formats.append([b'%s'] * len(l))
562 formats.append([b'%s'] * len(l))
563 pieces.append(l)
563 pieces.append(l)
564
564
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566 fm.startitem()
566 fm.startitem()
567 fm.context(fctx=n.fctx)
567 fm.context(fctx=n.fctx)
568 fm.write(fields, b"".join(f), *p)
568 fm.write(fields, b"".join(f), *p)
569 if n.skip:
569 if n.skip:
570 fmt = b"* %s"
570 fmt = b"* %s"
571 else:
571 else:
572 fmt = b": %s"
572 fmt = b": %s"
573 fm.write(b'line', fmt, n.text)
573 fm.write(b'line', fmt, n.text)
574
574
575 if not lines[-1].text.endswith(b'\n'):
575 if not lines[-1].text.endswith(b'\n'):
576 fm.plain(b'\n')
576 fm.plain(b'\n')
577 fm.end()
577 fm.end()
578
578
579 rootfm.end()
579 rootfm.end()
580
580
581
581
582 @command(
582 @command(
583 b'archive',
583 b'archive',
584 [
584 [
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586 (
586 (
587 b'p',
587 b'p',
588 b'prefix',
588 b'prefix',
589 b'',
589 b'',
590 _(b'directory prefix for files in archive'),
590 _(b'directory prefix for files in archive'),
591 _(b'PREFIX'),
591 _(b'PREFIX'),
592 ),
592 ),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 ]
595 ]
596 + subrepoopts
596 + subrepoopts
597 + walkopts,
597 + walkopts,
598 _(b'[OPTION]... DEST'),
598 _(b'[OPTION]... DEST'),
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
600 )
600 )
601 def archive(ui, repo, dest, **opts):
601 def archive(ui, repo, dest, **opts):
602 '''create an unversioned archive of a repository revision
602 '''create an unversioned archive of a repository revision
603
603
604 By default, the revision used is the parent of the working
604 By default, the revision used is the parent of the working
605 directory; use -r/--rev to specify a different revision.
605 directory; use -r/--rev to specify a different revision.
606
606
607 The archive type is automatically detected based on file
607 The archive type is automatically detected based on file
608 extension (to override, use -t/--type).
608 extension (to override, use -t/--type).
609
609
610 .. container:: verbose
610 .. container:: verbose
611
611
612 Examples:
612 Examples:
613
613
614 - create a zip file containing the 1.0 release::
614 - create a zip file containing the 1.0 release::
615
615
616 hg archive -r 1.0 project-1.0.zip
616 hg archive -r 1.0 project-1.0.zip
617
617
618 - create a tarball excluding .hg files::
618 - create a tarball excluding .hg files::
619
619
620 hg archive project.tar.gz -X ".hg*"
620 hg archive project.tar.gz -X ".hg*"
621
621
622 Valid types are:
622 Valid types are:
623
623
624 :``files``: a directory full of files (default)
624 :``files``: a directory full of files (default)
625 :``tar``: tar archive, uncompressed
625 :``tar``: tar archive, uncompressed
626 :``tbz2``: tar archive, compressed using bzip2
626 :``tbz2``: tar archive, compressed using bzip2
627 :``tgz``: tar archive, compressed using gzip
627 :``tgz``: tar archive, compressed using gzip
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
629 :``uzip``: zip archive, uncompressed
629 :``uzip``: zip archive, uncompressed
630 :``zip``: zip archive, compressed using deflate
630 :``zip``: zip archive, compressed using deflate
631
631
632 The exact name of the destination archive or directory is given
632 The exact name of the destination archive or directory is given
633 using a format string; see :hg:`help export` for details.
633 using a format string; see :hg:`help export` for details.
634
634
635 Each member added to an archive file has a directory prefix
635 Each member added to an archive file has a directory prefix
636 prepended. Use -p/--prefix to specify a format string for the
636 prepended. Use -p/--prefix to specify a format string for the
637 prefix. The default is the basename of the archive, with suffixes
637 prefix. The default is the basename of the archive, with suffixes
638 removed.
638 removed.
639
639
640 Returns 0 on success.
640 Returns 0 on success.
641 '''
641 '''
642
642
643 opts = pycompat.byteskwargs(opts)
643 opts = pycompat.byteskwargs(opts)
644 rev = opts.get(b'rev')
644 rev = opts.get(b'rev')
645 if rev:
645 if rev:
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647 ctx = scmutil.revsingle(repo, rev)
647 ctx = scmutil.revsingle(repo, rev)
648 if not ctx:
648 if not ctx:
649 raise error.Abort(_(b'no working directory: please specify a revision'))
649 raise error.Abort(_(b'no working directory: please specify a revision'))
650 node = ctx.node()
650 node = ctx.node()
651 dest = cmdutil.makefilename(ctx, dest)
651 dest = cmdutil.makefilename(ctx, dest)
652 if os.path.realpath(dest) == repo.root:
652 if os.path.realpath(dest) == repo.root:
653 raise error.Abort(_(b'repository root cannot be destination'))
653 raise error.Abort(_(b'repository root cannot be destination'))
654
654
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
656 prefix = opts.get(b'prefix')
656 prefix = opts.get(b'prefix')
657
657
658 if dest == b'-':
658 if dest == b'-':
659 if kind == b'files':
659 if kind == b'files':
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
661 dest = cmdutil.makefileobj(ctx, dest)
661 dest = cmdutil.makefileobj(ctx, dest)
662 if not prefix:
662 if not prefix:
663 prefix = os.path.basename(repo.root) + b'-%h'
663 prefix = os.path.basename(repo.root) + b'-%h'
664
664
665 prefix = cmdutil.makefilename(ctx, prefix)
665 prefix = cmdutil.makefilename(ctx, prefix)
666 match = scmutil.match(ctx, [], opts)
666 match = scmutil.match(ctx, [], opts)
667 archival.archive(
667 archival.archive(
668 repo,
668 repo,
669 dest,
669 dest,
670 node,
670 node,
671 kind,
671 kind,
672 not opts.get(b'no_decode'),
672 not opts.get(b'no_decode'),
673 match,
673 match,
674 prefix,
674 prefix,
675 subrepos=opts.get(b'subrepos'),
675 subrepos=opts.get(b'subrepos'),
676 )
676 )
677
677
678
678
679 @command(
679 @command(
680 b'backout',
680 b'backout',
681 [
681 [
682 (
682 (
683 b'',
683 b'',
684 b'merge',
684 b'merge',
685 None,
685 None,
686 _(b'merge with old dirstate parent after backout'),
686 _(b'merge with old dirstate parent after backout'),
687 ),
687 ),
688 (
688 (
689 b'',
689 b'',
690 b'commit',
690 b'commit',
691 None,
691 None,
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
693 ),
693 ),
694 (b'', b'no-commit', None, _(b'do not commit')),
694 (b'', b'no-commit', None, _(b'do not commit')),
695 (
695 (
696 b'',
696 b'',
697 b'parent',
697 b'parent',
698 b'',
698 b'',
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
700 _(b'REV'),
700 _(b'REV'),
701 ),
701 ),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 ]
704 ]
705 + mergetoolopts
705 + mergetoolopts
706 + walkopts
706 + walkopts
707 + commitopts
707 + commitopts
708 + commitopts2,
708 + commitopts2,
709 _(b'[OPTION]... [-r] REV'),
709 _(b'[OPTION]... [-r] REV'),
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
711 )
711 )
712 def backout(ui, repo, node=None, rev=None, **opts):
712 def backout(ui, repo, node=None, rev=None, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Prepare a new changeset with the effect of REV undone in the
715 Prepare a new changeset with the effect of REV undone in the
716 current working directory. If no conflicts were encountered,
716 current working directory. If no conflicts were encountered,
717 it will be committed immediately.
717 it will be committed immediately.
718
718
719 If REV is the parent of the working directory, then this new changeset
719 If REV is the parent of the working directory, then this new changeset
720 is committed automatically (unless --no-commit is specified).
720 is committed automatically (unless --no-commit is specified).
721
721
722 .. note::
722 .. note::
723
723
724 :hg:`backout` cannot be used to fix either an unwanted or
724 :hg:`backout` cannot be used to fix either an unwanted or
725 incorrect merge.
725 incorrect merge.
726
726
727 .. container:: verbose
727 .. container:: verbose
728
728
729 Examples:
729 Examples:
730
730
731 - Reverse the effect of the parent of the working directory.
731 - Reverse the effect of the parent of the working directory.
732 This backout will be committed immediately::
732 This backout will be committed immediately::
733
733
734 hg backout -r .
734 hg backout -r .
735
735
736 - Reverse the effect of previous bad revision 23::
736 - Reverse the effect of previous bad revision 23::
737
737
738 hg backout -r 23
738 hg backout -r 23
739
739
740 - Reverse the effect of previous bad revision 23 and
740 - Reverse the effect of previous bad revision 23 and
741 leave changes uncommitted::
741 leave changes uncommitted::
742
742
743 hg backout -r 23 --no-commit
743 hg backout -r 23 --no-commit
744 hg commit -m "Backout revision 23"
744 hg commit -m "Backout revision 23"
745
745
746 By default, the pending changeset will have one parent,
746 By default, the pending changeset will have one parent,
747 maintaining a linear history. With --merge, the pending
747 maintaining a linear history. With --merge, the pending
748 changeset will instead have two parents: the old parent of the
748 changeset will instead have two parents: the old parent of the
749 working directory and a new child of REV that simply undoes REV.
749 working directory and a new child of REV that simply undoes REV.
750
750
751 Before version 1.7, the behavior without --merge was equivalent
751 Before version 1.7, the behavior without --merge was equivalent
752 to specifying --merge followed by :hg:`update --clean .` to
752 to specifying --merge followed by :hg:`update --clean .` to
753 cancel the merge and leave the child of REV as a head to be
753 cancel the merge and leave the child of REV as a head to be
754 merged separately.
754 merged separately.
755
755
756 See :hg:`help dates` for a list of formats valid for -d/--date.
756 See :hg:`help dates` for a list of formats valid for -d/--date.
757
757
758 See :hg:`help revert` for a way to restore files to the state
758 See :hg:`help revert` for a way to restore files to the state
759 of another revision.
759 of another revision.
760
760
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
762 files.
762 files.
763 '''
763 '''
764 with repo.wlock(), repo.lock():
764 with repo.wlock(), repo.lock():
765 return _dobackout(ui, repo, node, rev, **opts)
765 return _dobackout(ui, repo, node, rev, **opts)
766
766
767
767
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
769 opts = pycompat.byteskwargs(opts)
769 opts = pycompat.byteskwargs(opts)
770 if opts.get(b'commit') and opts.get(b'no_commit'):
770 if opts.get(b'commit') and opts.get(b'no_commit'):
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
772 if opts.get(b'merge') and opts.get(b'no_commit'):
772 if opts.get(b'merge') and opts.get(b'no_commit'):
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
774
774
775 if rev and node:
775 if rev and node:
776 raise error.Abort(_(b"please specify just one revision"))
776 raise error.Abort(_(b"please specify just one revision"))
777
777
778 if not rev:
778 if not rev:
779 rev = node
779 rev = node
780
780
781 if not rev:
781 if not rev:
782 raise error.Abort(_(b"please specify a revision to backout"))
782 raise error.Abort(_(b"please specify a revision to backout"))
783
783
784 date = opts.get(b'date')
784 date = opts.get(b'date')
785 if date:
785 if date:
786 opts[b'date'] = dateutil.parsedate(date)
786 opts[b'date'] = dateutil.parsedate(date)
787
787
788 cmdutil.checkunfinished(repo)
788 cmdutil.checkunfinished(repo)
789 cmdutil.bailifchanged(repo)
789 cmdutil.bailifchanged(repo)
790 node = scmutil.revsingle(repo, rev).node()
790 node = scmutil.revsingle(repo, rev).node()
791
791
792 op1, op2 = repo.dirstate.parents()
792 op1, op2 = repo.dirstate.parents()
793 if not repo.changelog.isancestor(node, op1):
793 if not repo.changelog.isancestor(node, op1):
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
795
795
796 p1, p2 = repo.changelog.parents(node)
796 p1, p2 = repo.changelog.parents(node)
797 if p1 == nullid:
797 if p1 == nullid:
798 raise error.Abort(_(b'cannot backout a change with no parents'))
798 raise error.Abort(_(b'cannot backout a change with no parents'))
799 if p2 != nullid:
799 if p2 != nullid:
800 if not opts.get(b'parent'):
800 if not opts.get(b'parent'):
801 raise error.Abort(_(b'cannot backout a merge changeset'))
801 raise error.Abort(_(b'cannot backout a merge changeset'))
802 p = repo.lookup(opts[b'parent'])
802 p = repo.lookup(opts[b'parent'])
803 if p not in (p1, p2):
803 if p not in (p1, p2):
804 raise error.Abort(
804 raise error.Abort(
805 _(b'%s is not a parent of %s') % (short(p), short(node))
805 _(b'%s is not a parent of %s') % (short(p), short(node))
806 )
806 )
807 parent = p
807 parent = p
808 else:
808 else:
809 if opts.get(b'parent'):
809 if opts.get(b'parent'):
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
811 parent = p1
811 parent = p1
812
812
813 # the backout should appear on the same branch
813 # the backout should appear on the same branch
814 branch = repo.dirstate.branch()
814 branch = repo.dirstate.branch()
815 bheads = repo.branchheads(branch)
815 bheads = repo.branchheads(branch)
816 rctx = scmutil.revsingle(repo, hex(parent))
816 rctx = scmutil.revsingle(repo, hex(parent))
817 if not opts.get(b'merge') and op1 != node:
817 if not opts.get(b'merge') and op1 != node:
818 with dirstateguard.dirstateguard(repo, b'backout'):
818 with dirstateguard.dirstateguard(repo, b'backout'):
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
820 with ui.configoverride(overrides, b'backout'):
820 with ui.configoverride(overrides, b'backout'):
821 stats = mergemod.update(
821 stats = mergemod.update(
822 repo,
822 repo,
823 parent,
823 parent,
824 branchmerge=True,
824 branchmerge=True,
825 force=True,
825 force=True,
826 ancestor=node,
826 ancestor=node,
827 mergeancestor=False,
827 mergeancestor=False,
828 )
828 )
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
860 if not newnode:
860 if not newnode:
861 ui.status(_(b"nothing changed\n"))
861 ui.status(_(b"nothing changed\n"))
862 return 1
862 return 1
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
864
864
865 def nice(node):
865 def nice(node):
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
867
867
868 ui.status(
868 ui.status(
869 _(b'changeset %s backs out changeset %s\n')
869 _(b'changeset %s backs out changeset %s\n')
870 % (nice(repo.changelog.tip()), nice(node))
870 % (nice(repo.changelog.tip()), nice(node))
871 )
871 )
872 if opts.get(b'merge') and op1 != node:
872 if opts.get(b'merge') and op1 != node:
873 hg.clean(repo, op1, show_stats=False)
873 hg.clean(repo, op1, show_stats=False)
874 ui.status(
874 ui.status(
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
876 )
876 )
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 with ui.configoverride(overrides, b'backout'):
878 with ui.configoverride(overrides, b'backout'):
879 return hg.merge(repo[b'tip'])
879 return hg.merge(repo[b'tip'])
880 return 0
880 return 0
881
881
882
882
883 @command(
883 @command(
884 b'bisect',
884 b'bisect',
885 [
885 [
886 (b'r', b'reset', False, _(b'reset bisect state')),
886 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'g', b'good', False, _(b'mark changeset good')),
887 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (
891 (
892 b'c',
892 b'c',
893 b'command',
893 b'command',
894 b'',
894 b'',
895 _(b'use command to check changeset state'),
895 _(b'use command to check changeset state'),
896 _(b'CMD'),
896 _(b'CMD'),
897 ),
897 ),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
899 ],
899 ],
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 )
902 )
903 def bisect(
903 def bisect(
904 ui,
904 ui,
905 repo,
905 repo,
906 rev=None,
906 rev=None,
907 extra=None,
907 extra=None,
908 command=None,
908 command=None,
909 reset=None,
909 reset=None,
910 good=None,
910 good=None,
911 bad=None,
911 bad=None,
912 skip=None,
912 skip=None,
913 extend=None,
913 extend=None,
914 noupdate=None,
914 noupdate=None,
915 ):
915 ):
916 """subdivision search of changesets
916 """subdivision search of changesets
917
917
918 This command helps to find changesets which introduce problems. To
918 This command helps to find changesets which introduce problems. To
919 use, mark the earliest changeset you know exhibits the problem as
919 use, mark the earliest changeset you know exhibits the problem as
920 bad, then mark the latest changeset which is free from the problem
920 bad, then mark the latest changeset which is free from the problem
921 as good. Bisect will update your working directory to a revision
921 as good. Bisect will update your working directory to a revision
922 for testing (unless the -U/--noupdate option is specified). Once
922 for testing (unless the -U/--noupdate option is specified). Once
923 you have performed tests, mark the working directory as good or
923 you have performed tests, mark the working directory as good or
924 bad, and bisect will either update to another candidate changeset
924 bad, and bisect will either update to another candidate changeset
925 or announce that it has found the bad revision.
925 or announce that it has found the bad revision.
926
926
927 As a shortcut, you can also use the revision argument to mark a
927 As a shortcut, you can also use the revision argument to mark a
928 revision as good or bad without checking it out first.
928 revision as good or bad without checking it out first.
929
929
930 If you supply a command, it will be used for automatic bisection.
930 If you supply a command, it will be used for automatic bisection.
931 The environment variable HG_NODE will contain the ID of the
931 The environment variable HG_NODE will contain the ID of the
932 changeset being tested. The exit status of the command will be
932 changeset being tested. The exit status of the command will be
933 used to mark revisions as good or bad: status 0 means good, 125
933 used to mark revisions as good or bad: status 0 means good, 125
934 means to skip the revision, 127 (command not found) will abort the
934 means to skip the revision, 127 (command not found) will abort the
935 bisection, and any other non-zero exit status means the revision
935 bisection, and any other non-zero exit status means the revision
936 is bad.
936 is bad.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Some examples:
940 Some examples:
941
941
942 - start a bisection with known bad revision 34, and good revision 12::
942 - start a bisection with known bad revision 34, and good revision 12::
943
943
944 hg bisect --bad 34
944 hg bisect --bad 34
945 hg bisect --good 12
945 hg bisect --good 12
946
946
947 - advance the current bisection by marking current revision as good or
947 - advance the current bisection by marking current revision as good or
948 bad::
948 bad::
949
949
950 hg bisect --good
950 hg bisect --good
951 hg bisect --bad
951 hg bisect --bad
952
952
953 - mark the current revision, or a known revision, to be skipped (e.g. if
953 - mark the current revision, or a known revision, to be skipped (e.g. if
954 that revision is not usable because of another issue)::
954 that revision is not usable because of another issue)::
955
955
956 hg bisect --skip
956 hg bisect --skip
957 hg bisect --skip 23
957 hg bisect --skip 23
958
958
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960
960
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962
962
963 - forget the current bisection::
963 - forget the current bisection::
964
964
965 hg bisect --reset
965 hg bisect --reset
966
966
967 - use 'make && make tests' to automatically find the first broken
967 - use 'make && make tests' to automatically find the first broken
968 revision::
968 revision::
969
969
970 hg bisect --reset
970 hg bisect --reset
971 hg bisect --bad 34
971 hg bisect --bad 34
972 hg bisect --good 12
972 hg bisect --good 12
973 hg bisect --command "make && make tests"
973 hg bisect --command "make && make tests"
974
974
975 - see all changesets whose states are already known in the current
975 - see all changesets whose states are already known in the current
976 bisection::
976 bisection::
977
977
978 hg log -r "bisect(pruned)"
978 hg log -r "bisect(pruned)"
979
979
980 - see the changeset currently being bisected (especially useful
980 - see the changeset currently being bisected (especially useful
981 if running with -U/--noupdate)::
981 if running with -U/--noupdate)::
982
982
983 hg log -r "bisect(current)"
983 hg log -r "bisect(current)"
984
984
985 - see all changesets that took part in the current bisection::
985 - see all changesets that took part in the current bisection::
986
986
987 hg log -r "bisect(range)"
987 hg log -r "bisect(range)"
988
988
989 - you can even get a nice graph::
989 - you can even get a nice graph::
990
990
991 hg log --graph -r "bisect(range)"
991 hg log --graph -r "bisect(range)"
992
992
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994
994
995 Returns 0 on success.
995 Returns 0 on success.
996 """
996 """
997 # backward compatibility
997 # backward compatibility
998 if rev in b"good bad reset init".split():
998 if rev in b"good bad reset init".split():
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1000 cmd, rev, extra = rev, extra, None
1000 cmd, rev, extra = rev, extra, None
1001 if cmd == b"good":
1001 if cmd == b"good":
1002 good = True
1002 good = True
1003 elif cmd == b"bad":
1003 elif cmd == b"bad":
1004 bad = True
1004 bad = True
1005 else:
1005 else:
1006 reset = True
1006 reset = True
1007 elif extra:
1007 elif extra:
1008 raise error.Abort(_(b'incompatible arguments'))
1008 raise error.Abort(_(b'incompatible arguments'))
1009
1009
1010 incompatibles = {
1010 incompatibles = {
1011 b'--bad': bad,
1011 b'--bad': bad,
1012 b'--command': bool(command),
1012 b'--command': bool(command),
1013 b'--extend': extend,
1013 b'--extend': extend,
1014 b'--good': good,
1014 b'--good': good,
1015 b'--reset': reset,
1015 b'--reset': reset,
1016 b'--skip': skip,
1016 b'--skip': skip,
1017 }
1017 }
1018
1018
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1020
1020
1021 if len(enabled) > 1:
1021 if len(enabled) > 1:
1022 raise error.Abort(
1022 raise error.Abort(
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1024 )
1024 )
1025
1025
1026 if reset:
1026 if reset:
1027 hbisect.resetstate(repo)
1027 hbisect.resetstate(repo)
1028 return
1028 return
1029
1029
1030 state = hbisect.load_state(repo)
1030 state = hbisect.load_state(repo)
1031
1031
1032 # update state
1032 # update state
1033 if good or bad or skip:
1033 if good or bad or skip:
1034 if rev:
1034 if rev:
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1036 else:
1036 else:
1037 nodes = [repo.lookup(b'.')]
1037 nodes = [repo.lookup(b'.')]
1038 if good:
1038 if good:
1039 state[b'good'] += nodes
1039 state[b'good'] += nodes
1040 elif bad:
1040 elif bad:
1041 state[b'bad'] += nodes
1041 state[b'bad'] += nodes
1042 elif skip:
1042 elif skip:
1043 state[b'skip'] += nodes
1043 state[b'skip'] += nodes
1044 hbisect.save_state(repo, state)
1044 hbisect.save_state(repo, state)
1045 if not (state[b'good'] and state[b'bad']):
1045 if not (state[b'good'] and state[b'bad']):
1046 return
1046 return
1047
1047
1048 def mayupdate(repo, node, show_stats=True):
1048 def mayupdate(repo, node, show_stats=True):
1049 """common used update sequence"""
1049 """common used update sequence"""
1050 if noupdate:
1050 if noupdate:
1051 return
1051 return
1052 cmdutil.checkunfinished(repo)
1052 cmdutil.checkunfinished(repo)
1053 cmdutil.bailifchanged(repo)
1053 cmdutil.bailifchanged(repo)
1054 return hg.clean(repo, node, show_stats=show_stats)
1054 return hg.clean(repo, node, show_stats=show_stats)
1055
1055
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1057
1057
1058 if command:
1058 if command:
1059 changesets = 1
1059 changesets = 1
1060 if noupdate:
1060 if noupdate:
1061 try:
1061 try:
1062 node = state[b'current'][0]
1062 node = state[b'current'][0]
1063 except LookupError:
1063 except LookupError:
1064 raise error.Abort(
1064 raise error.Abort(
1065 _(
1065 _(
1066 b'current bisect revision is unknown - '
1066 b'current bisect revision is unknown - '
1067 b'start a new bisect to fix'
1067 b'start a new bisect to fix'
1068 )
1068 )
1069 )
1069 )
1070 else:
1070 else:
1071 node, p2 = repo.dirstate.parents()
1071 node, p2 = repo.dirstate.parents()
1072 if p2 != nullid:
1072 if p2 != nullid:
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1074 if rev:
1074 if rev:
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1076 with hbisect.restore_state(repo, state, node):
1076 with hbisect.restore_state(repo, state, node):
1077 while changesets:
1077 while changesets:
1078 # update state
1078 # update state
1079 state[b'current'] = [node]
1079 state[b'current'] = [node]
1080 hbisect.save_state(repo, state)
1080 hbisect.save_state(repo, state)
1081 status = ui.system(
1081 status = ui.system(
1082 command,
1082 command,
1083 environ={b'HG_NODE': hex(node)},
1083 environ={b'HG_NODE': hex(node)},
1084 blockedtag=b'bisect_check',
1084 blockedtag=b'bisect_check',
1085 )
1085 )
1086 if status == 125:
1086 if status == 125:
1087 transition = b"skip"
1087 transition = b"skip"
1088 elif status == 0:
1088 elif status == 0:
1089 transition = b"good"
1089 transition = b"good"
1090 # status < 0 means process was killed
1090 # status < 0 means process was killed
1091 elif status == 127:
1091 elif status == 127:
1092 raise error.Abort(_(b"failed to execute %s") % command)
1092 raise error.Abort(_(b"failed to execute %s") % command)
1093 elif status < 0:
1093 elif status < 0:
1094 raise error.Abort(_(b"%s killed") % command)
1094 raise error.Abort(_(b"%s killed") % command)
1095 else:
1095 else:
1096 transition = b"bad"
1096 transition = b"bad"
1097 state[transition].append(node)
1097 state[transition].append(node)
1098 ctx = repo[node]
1098 ctx = repo[node]
1099 ui.status(
1099 ui.status(
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1101 )
1101 )
1102 hbisect.checkstate(state)
1102 hbisect.checkstate(state)
1103 # bisect
1103 # bisect
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1105 # update to next check
1105 # update to next check
1106 node = nodes[0]
1106 node = nodes[0]
1107 mayupdate(repo, node, show_stats=False)
1107 mayupdate(repo, node, show_stats=False)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1109 return
1109 return
1110
1110
1111 hbisect.checkstate(state)
1111 hbisect.checkstate(state)
1112
1112
1113 # actually bisect
1113 # actually bisect
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1115 if extend:
1115 if extend:
1116 if not changesets:
1116 if not changesets:
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1118 if extendnode is not None:
1118 if extendnode is not None:
1119 ui.write(
1119 ui.write(
1120 _(b"Extending search to changeset %d:%s\n")
1120 _(b"Extending search to changeset %d:%s\n")
1121 % (extendnode.rev(), extendnode)
1121 % (extendnode.rev(), extendnode)
1122 )
1122 )
1123 state[b'current'] = [extendnode.node()]
1123 state[b'current'] = [extendnode.node()]
1124 hbisect.save_state(repo, state)
1124 hbisect.save_state(repo, state)
1125 return mayupdate(repo, extendnode.node())
1125 return mayupdate(repo, extendnode.node())
1126 raise error.Abort(_(b"nothing to extend"))
1126 raise error.Abort(_(b"nothing to extend"))
1127
1127
1128 if changesets == 0:
1128 if changesets == 0:
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1130 else:
1130 else:
1131 assert len(nodes) == 1 # only a single node can be tested next
1131 assert len(nodes) == 1 # only a single node can be tested next
1132 node = nodes[0]
1132 node = nodes[0]
1133 # compute the approximate number of remaining tests
1133 # compute the approximate number of remaining tests
1134 tests, size = 0, 2
1134 tests, size = 0, 2
1135 while size <= changesets:
1135 while size <= changesets:
1136 tests, size = tests + 1, size * 2
1136 tests, size = tests + 1, size * 2
1137 rev = repo.changelog.rev(node)
1137 rev = repo.changelog.rev(node)
1138 ui.write(
1138 ui.write(
1139 _(
1139 _(
1140 b"Testing changeset %d:%s "
1140 b"Testing changeset %d:%s "
1141 b"(%d changesets remaining, ~%d tests)\n"
1141 b"(%d changesets remaining, ~%d tests)\n"
1142 )
1142 )
1143 % (rev, short(node), changesets, tests)
1143 % (rev, short(node), changesets, tests)
1144 )
1144 )
1145 state[b'current'] = [node]
1145 state[b'current'] = [node]
1146 hbisect.save_state(repo, state)
1146 hbisect.save_state(repo, state)
1147 return mayupdate(repo, node)
1147 return mayupdate(repo, node)
1148
1148
1149
1149
1150 @command(
1150 @command(
1151 b'bookmarks|bookmark',
1151 b'bookmarks|bookmark',
1152 [
1152 [
1153 (b'f', b'force', False, _(b'force')),
1153 (b'f', b'force', False, _(b'force')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1159 ]
1159 ]
1160 + formatteropts,
1160 + formatteropts,
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1163 )
1163 )
1164 def bookmark(ui, repo, *names, **opts):
1164 def bookmark(ui, repo, *names, **opts):
1165 '''create a new bookmark or list existing bookmarks
1165 '''create a new bookmark or list existing bookmarks
1166
1166
1167 Bookmarks are labels on changesets to help track lines of development.
1167 Bookmarks are labels on changesets to help track lines of development.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1170
1170
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1172 The active bookmark is indicated with a '*'.
1172 The active bookmark is indicated with a '*'.
1173 When a commit is made, the active bookmark will advance to the new commit.
1173 When a commit is made, the active bookmark will advance to the new commit.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1175 Updating away from a bookmark will cause it to be deactivated.
1175 Updating away from a bookmark will cause it to be deactivated.
1176
1176
1177 Bookmarks can be pushed and pulled between repositories (see
1177 Bookmarks can be pushed and pulled between repositories (see
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1180 be created. Using :hg:`merge` will resolve the divergence.
1180 be created. Using :hg:`merge` will resolve the divergence.
1181
1181
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1183 the active bookmark's name.
1183 the active bookmark's name.
1184
1184
1185 A bookmark named '@' has the special property that :hg:`clone` will
1185 A bookmark named '@' has the special property that :hg:`clone` will
1186 check it out by default if it exists.
1186 check it out by default if it exists.
1187
1187
1188 .. container:: verbose
1188 .. container:: verbose
1189
1189
1190 Template:
1190 Template:
1191
1191
1192 The following keywords are supported in addition to the common template
1192 The following keywords are supported in addition to the common template
1193 keywords and functions such as ``{bookmark}``. See also
1193 keywords and functions such as ``{bookmark}``. See also
1194 :hg:`help templates`.
1194 :hg:`help templates`.
1195
1195
1196 :active: Boolean. True if the bookmark is active.
1196 :active: Boolean. True if the bookmark is active.
1197
1197
1198 Examples:
1198 Examples:
1199
1199
1200 - create an active bookmark for a new line of development::
1200 - create an active bookmark for a new line of development::
1201
1201
1202 hg book new-feature
1202 hg book new-feature
1203
1203
1204 - create an inactive bookmark as a place marker::
1204 - create an inactive bookmark as a place marker::
1205
1205
1206 hg book -i reviewed
1206 hg book -i reviewed
1207
1207
1208 - create an inactive bookmark on another changeset::
1208 - create an inactive bookmark on another changeset::
1209
1209
1210 hg book -r .^ tested
1210 hg book -r .^ tested
1211
1211
1212 - rename bookmark turkey to dinner::
1212 - rename bookmark turkey to dinner::
1213
1213
1214 hg book -m turkey dinner
1214 hg book -m turkey dinner
1215
1215
1216 - move the '@' bookmark from another branch::
1216 - move the '@' bookmark from another branch::
1217
1217
1218 hg book -f @
1218 hg book -f @
1219
1219
1220 - print only the active bookmark name::
1220 - print only the active bookmark name::
1221
1221
1222 hg book -ql .
1222 hg book -ql .
1223 '''
1223 '''
1224 opts = pycompat.byteskwargs(opts)
1224 opts = pycompat.byteskwargs(opts)
1225 force = opts.get(b'force')
1225 force = opts.get(b'force')
1226 rev = opts.get(b'rev')
1226 rev = opts.get(b'rev')
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1228
1228
1229 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1229 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1230 if action:
1230 if action:
1231 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1231 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1232 elif names or rev:
1232 elif names or rev:
1233 action = b'add'
1233 action = b'add'
1234 elif inactive:
1234 elif inactive:
1235 action = b'inactive' # meaning deactivate
1235 action = b'inactive' # meaning deactivate
1236 else:
1236 else:
1237 action = b'list'
1237 action = b'list'
1238
1238
1239 cmdutil.check_incompatible_arguments(
1239 cmdutil.check_incompatible_arguments(
1240 opts, b'inactive', [b'delete', b'list']
1240 opts, b'inactive', [b'delete', b'list']
1241 )
1241 )
1242 if not names and action in {b'add', b'delete'}:
1242 if not names and action in {b'add', b'delete'}:
1243 raise error.Abort(_(b"bookmark name required"))
1243 raise error.Abort(_(b"bookmark name required"))
1244
1244
1245 if action in {b'add', b'delete', b'rename', b'inactive'}:
1245 if action in {b'add', b'delete', b'rename', b'inactive'}:
1246 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1246 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1247 if action == b'delete':
1247 if action == b'delete':
1248 names = pycompat.maplist(repo._bookmarks.expandname, names)
1248 names = pycompat.maplist(repo._bookmarks.expandname, names)
1249 bookmarks.delete(repo, tr, names)
1249 bookmarks.delete(repo, tr, names)
1250 elif action == b'rename':
1250 elif action == b'rename':
1251 if not names:
1251 if not names:
1252 raise error.Abort(_(b"new bookmark name required"))
1252 raise error.Abort(_(b"new bookmark name required"))
1253 elif len(names) > 1:
1253 elif len(names) > 1:
1254 raise error.Abort(_(b"only one new bookmark name allowed"))
1254 raise error.Abort(_(b"only one new bookmark name allowed"))
1255 oldname = repo._bookmarks.expandname(opts[b'rename'])
1255 oldname = repo._bookmarks.expandname(opts[b'rename'])
1256 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1256 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1257 elif action == b'add':
1257 elif action == b'add':
1258 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1258 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1259 elif action == b'inactive':
1259 elif action == b'inactive':
1260 if len(repo._bookmarks) == 0:
1260 if len(repo._bookmarks) == 0:
1261 ui.status(_(b"no bookmarks set\n"))
1261 ui.status(_(b"no bookmarks set\n"))
1262 elif not repo._activebookmark:
1262 elif not repo._activebookmark:
1263 ui.status(_(b"no active bookmark\n"))
1263 ui.status(_(b"no active bookmark\n"))
1264 else:
1264 else:
1265 bookmarks.deactivate(repo)
1265 bookmarks.deactivate(repo)
1266 elif action == b'list':
1266 elif action == b'list':
1267 names = pycompat.maplist(repo._bookmarks.expandname, names)
1267 names = pycompat.maplist(repo._bookmarks.expandname, names)
1268 with ui.formatter(b'bookmarks', opts) as fm:
1268 with ui.formatter(b'bookmarks', opts) as fm:
1269 bookmarks.printbookmarks(ui, repo, fm, names)
1269 bookmarks.printbookmarks(ui, repo, fm, names)
1270 else:
1270 else:
1271 raise error.ProgrammingError(b'invalid action: %s' % action)
1271 raise error.ProgrammingError(b'invalid action: %s' % action)
1272
1272
1273
1273
1274 @command(
1274 @command(
1275 b'branch',
1275 b'branch',
1276 [
1276 [
1277 (
1277 (
1278 b'f',
1278 b'f',
1279 b'force',
1279 b'force',
1280 None,
1280 None,
1281 _(b'set branch name even if it shadows an existing branch'),
1281 _(b'set branch name even if it shadows an existing branch'),
1282 ),
1282 ),
1283 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1283 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1284 (
1284 (
1285 b'r',
1285 b'r',
1286 b'rev',
1286 b'rev',
1287 [],
1287 [],
1288 _(b'change branches of the given revs (EXPERIMENTAL)'),
1288 _(b'change branches of the given revs (EXPERIMENTAL)'),
1289 ),
1289 ),
1290 ],
1290 ],
1291 _(b'[-fC] [NAME]'),
1291 _(b'[-fC] [NAME]'),
1292 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1292 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1293 )
1293 )
1294 def branch(ui, repo, label=None, **opts):
1294 def branch(ui, repo, label=None, **opts):
1295 """set or show the current branch name
1295 """set or show the current branch name
1296
1296
1297 .. note::
1297 .. note::
1298
1298
1299 Branch names are permanent and global. Use :hg:`bookmark` to create a
1299 Branch names are permanent and global. Use :hg:`bookmark` to create a
1300 light-weight bookmark instead. See :hg:`help glossary` for more
1300 light-weight bookmark instead. See :hg:`help glossary` for more
1301 information about named branches and bookmarks.
1301 information about named branches and bookmarks.
1302
1302
1303 With no argument, show the current branch name. With one argument,
1303 With no argument, show the current branch name. With one argument,
1304 set the working directory branch name (the branch will not exist
1304 set the working directory branch name (the branch will not exist
1305 in the repository until the next commit). Standard practice
1305 in the repository until the next commit). Standard practice
1306 recommends that primary development take place on the 'default'
1306 recommends that primary development take place on the 'default'
1307 branch.
1307 branch.
1308
1308
1309 Unless -f/--force is specified, branch will not let you set a
1309 Unless -f/--force is specified, branch will not let you set a
1310 branch name that already exists.
1310 branch name that already exists.
1311
1311
1312 Use -C/--clean to reset the working directory branch to that of
1312 Use -C/--clean to reset the working directory branch to that of
1313 the parent of the working directory, negating a previous branch
1313 the parent of the working directory, negating a previous branch
1314 change.
1314 change.
1315
1315
1316 Use the command :hg:`update` to switch to an existing branch. Use
1316 Use the command :hg:`update` to switch to an existing branch. Use
1317 :hg:`commit --close-branch` to mark this branch head as closed.
1317 :hg:`commit --close-branch` to mark this branch head as closed.
1318 When all heads of a branch are closed, the branch will be
1318 When all heads of a branch are closed, the branch will be
1319 considered closed.
1319 considered closed.
1320
1320
1321 Returns 0 on success.
1321 Returns 0 on success.
1322 """
1322 """
1323 opts = pycompat.byteskwargs(opts)
1323 opts = pycompat.byteskwargs(opts)
1324 revs = opts.get(b'rev')
1324 revs = opts.get(b'rev')
1325 if label:
1325 if label:
1326 label = label.strip()
1326 label = label.strip()
1327
1327
1328 if not opts.get(b'clean') and not label:
1328 if not opts.get(b'clean') and not label:
1329 if revs:
1329 if revs:
1330 raise error.Abort(_(b"no branch name specified for the revisions"))
1330 raise error.Abort(_(b"no branch name specified for the revisions"))
1331 ui.write(b"%s\n" % repo.dirstate.branch())
1331 ui.write(b"%s\n" % repo.dirstate.branch())
1332 return
1332 return
1333
1333
1334 with repo.wlock():
1334 with repo.wlock():
1335 if opts.get(b'clean'):
1335 if opts.get(b'clean'):
1336 label = repo[b'.'].branch()
1336 label = repo[b'.'].branch()
1337 repo.dirstate.setbranch(label)
1337 repo.dirstate.setbranch(label)
1338 ui.status(_(b'reset working directory to branch %s\n') % label)
1338 ui.status(_(b'reset working directory to branch %s\n') % label)
1339 elif label:
1339 elif label:
1340
1340
1341 scmutil.checknewlabel(repo, label, b'branch')
1341 scmutil.checknewlabel(repo, label, b'branch')
1342 if revs:
1342 if revs:
1343 return cmdutil.changebranch(ui, repo, revs, label, opts)
1343 return cmdutil.changebranch(ui, repo, revs, label, opts)
1344
1344
1345 if not opts.get(b'force') and label in repo.branchmap():
1345 if not opts.get(b'force') and label in repo.branchmap():
1346 if label not in [p.branch() for p in repo[None].parents()]:
1346 if label not in [p.branch() for p in repo[None].parents()]:
1347 raise error.Abort(
1347 raise error.Abort(
1348 _(b'a branch of the same name already exists'),
1348 _(b'a branch of the same name already exists'),
1349 # i18n: "it" refers to an existing branch
1349 # i18n: "it" refers to an existing branch
1350 hint=_(b"use 'hg update' to switch to it"),
1350 hint=_(b"use 'hg update' to switch to it"),
1351 )
1351 )
1352
1352
1353 repo.dirstate.setbranch(label)
1353 repo.dirstate.setbranch(label)
1354 ui.status(_(b'marked working directory as branch %s\n') % label)
1354 ui.status(_(b'marked working directory as branch %s\n') % label)
1355
1355
1356 # find any open named branches aside from default
1356 # find any open named branches aside from default
1357 for n, h, t, c in repo.branchmap().iterbranches():
1357 for n, h, t, c in repo.branchmap().iterbranches():
1358 if n != b"default" and not c:
1358 if n != b"default" and not c:
1359 return 0
1359 return 0
1360 ui.status(
1360 ui.status(
1361 _(
1361 _(
1362 b'(branches are permanent and global, '
1362 b'(branches are permanent and global, '
1363 b'did you want a bookmark?)\n'
1363 b'did you want a bookmark?)\n'
1364 )
1364 )
1365 )
1365 )
1366
1366
1367
1367
1368 @command(
1368 @command(
1369 b'branches',
1369 b'branches',
1370 [
1370 [
1371 (
1371 (
1372 b'a',
1372 b'a',
1373 b'active',
1373 b'active',
1374 False,
1374 False,
1375 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1375 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1376 ),
1376 ),
1377 (b'c', b'closed', False, _(b'show normal and closed branches')),
1377 (b'c', b'closed', False, _(b'show normal and closed branches')),
1378 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1378 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1379 ]
1379 ]
1380 + formatteropts,
1380 + formatteropts,
1381 _(b'[-c]'),
1381 _(b'[-c]'),
1382 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1382 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1383 intents={INTENT_READONLY},
1383 intents={INTENT_READONLY},
1384 )
1384 )
1385 def branches(ui, repo, active=False, closed=False, **opts):
1385 def branches(ui, repo, active=False, closed=False, **opts):
1386 """list repository named branches
1386 """list repository named branches
1387
1387
1388 List the repository's named branches, indicating which ones are
1388 List the repository's named branches, indicating which ones are
1389 inactive. If -c/--closed is specified, also list branches which have
1389 inactive. If -c/--closed is specified, also list branches which have
1390 been marked closed (see :hg:`commit --close-branch`).
1390 been marked closed (see :hg:`commit --close-branch`).
1391
1391
1392 Use the command :hg:`update` to switch to an existing branch.
1392 Use the command :hg:`update` to switch to an existing branch.
1393
1393
1394 .. container:: verbose
1394 .. container:: verbose
1395
1395
1396 Template:
1396 Template:
1397
1397
1398 The following keywords are supported in addition to the common template
1398 The following keywords are supported in addition to the common template
1399 keywords and functions such as ``{branch}``. See also
1399 keywords and functions such as ``{branch}``. See also
1400 :hg:`help templates`.
1400 :hg:`help templates`.
1401
1401
1402 :active: Boolean. True if the branch is active.
1402 :active: Boolean. True if the branch is active.
1403 :closed: Boolean. True if the branch is closed.
1403 :closed: Boolean. True if the branch is closed.
1404 :current: Boolean. True if it is the current branch.
1404 :current: Boolean. True if it is the current branch.
1405
1405
1406 Returns 0.
1406 Returns 0.
1407 """
1407 """
1408
1408
1409 opts = pycompat.byteskwargs(opts)
1409 opts = pycompat.byteskwargs(opts)
1410 revs = opts.get(b'rev')
1410 revs = opts.get(b'rev')
1411 selectedbranches = None
1411 selectedbranches = None
1412 if revs:
1412 if revs:
1413 revs = scmutil.revrange(repo, revs)
1413 revs = scmutil.revrange(repo, revs)
1414 getbi = repo.revbranchcache().branchinfo
1414 getbi = repo.revbranchcache().branchinfo
1415 selectedbranches = {getbi(r)[0] for r in revs}
1415 selectedbranches = {getbi(r)[0] for r in revs}
1416
1416
1417 ui.pager(b'branches')
1417 ui.pager(b'branches')
1418 fm = ui.formatter(b'branches', opts)
1418 fm = ui.formatter(b'branches', opts)
1419 hexfunc = fm.hexfunc
1419 hexfunc = fm.hexfunc
1420
1420
1421 allheads = set(repo.heads())
1421 allheads = set(repo.heads())
1422 branches = []
1422 branches = []
1423 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1423 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1424 if selectedbranches is not None and tag not in selectedbranches:
1424 if selectedbranches is not None and tag not in selectedbranches:
1425 continue
1425 continue
1426 isactive = False
1426 isactive = False
1427 if not isclosed:
1427 if not isclosed:
1428 openheads = set(repo.branchmap().iteropen(heads))
1428 openheads = set(repo.branchmap().iteropen(heads))
1429 isactive = bool(openheads & allheads)
1429 isactive = bool(openheads & allheads)
1430 branches.append((tag, repo[tip], isactive, not isclosed))
1430 branches.append((tag, repo[tip], isactive, not isclosed))
1431 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1431 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1432
1432
1433 for tag, ctx, isactive, isopen in branches:
1433 for tag, ctx, isactive, isopen in branches:
1434 if active and not isactive:
1434 if active and not isactive:
1435 continue
1435 continue
1436 if isactive:
1436 if isactive:
1437 label = b'branches.active'
1437 label = b'branches.active'
1438 notice = b''
1438 notice = b''
1439 elif not isopen:
1439 elif not isopen:
1440 if not closed:
1440 if not closed:
1441 continue
1441 continue
1442 label = b'branches.closed'
1442 label = b'branches.closed'
1443 notice = _(b' (closed)')
1443 notice = _(b' (closed)')
1444 else:
1444 else:
1445 label = b'branches.inactive'
1445 label = b'branches.inactive'
1446 notice = _(b' (inactive)')
1446 notice = _(b' (inactive)')
1447 current = tag == repo.dirstate.branch()
1447 current = tag == repo.dirstate.branch()
1448 if current:
1448 if current:
1449 label = b'branches.current'
1449 label = b'branches.current'
1450
1450
1451 fm.startitem()
1451 fm.startitem()
1452 fm.write(b'branch', b'%s', tag, label=label)
1452 fm.write(b'branch', b'%s', tag, label=label)
1453 rev = ctx.rev()
1453 rev = ctx.rev()
1454 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1454 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1455 fmt = b' ' * padsize + b' %d:%s'
1455 fmt = b' ' * padsize + b' %d:%s'
1456 fm.condwrite(
1456 fm.condwrite(
1457 not ui.quiet,
1457 not ui.quiet,
1458 b'rev node',
1458 b'rev node',
1459 fmt,
1459 fmt,
1460 rev,
1460 rev,
1461 hexfunc(ctx.node()),
1461 hexfunc(ctx.node()),
1462 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1462 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1463 )
1463 )
1464 fm.context(ctx=ctx)
1464 fm.context(ctx=ctx)
1465 fm.data(active=isactive, closed=not isopen, current=current)
1465 fm.data(active=isactive, closed=not isopen, current=current)
1466 if not ui.quiet:
1466 if not ui.quiet:
1467 fm.plain(notice)
1467 fm.plain(notice)
1468 fm.plain(b'\n')
1468 fm.plain(b'\n')
1469 fm.end()
1469 fm.end()
1470
1470
1471
1471
1472 @command(
1472 @command(
1473 b'bundle',
1473 b'bundle',
1474 [
1474 [
1475 (
1475 (
1476 b'f',
1476 b'f',
1477 b'force',
1477 b'force',
1478 None,
1478 None,
1479 _(b'run even when the destination is unrelated'),
1479 _(b'run even when the destination is unrelated'),
1480 ),
1480 ),
1481 (
1481 (
1482 b'r',
1482 b'r',
1483 b'rev',
1483 b'rev',
1484 [],
1484 [],
1485 _(b'a changeset intended to be added to the destination'),
1485 _(b'a changeset intended to be added to the destination'),
1486 _(b'REV'),
1486 _(b'REV'),
1487 ),
1487 ),
1488 (
1488 (
1489 b'b',
1489 b'b',
1490 b'branch',
1490 b'branch',
1491 [],
1491 [],
1492 _(b'a specific branch you would like to bundle'),
1492 _(b'a specific branch you would like to bundle'),
1493 _(b'BRANCH'),
1493 _(b'BRANCH'),
1494 ),
1494 ),
1495 (
1495 (
1496 b'',
1496 b'',
1497 b'base',
1497 b'base',
1498 [],
1498 [],
1499 _(b'a base changeset assumed to be available at the destination'),
1499 _(b'a base changeset assumed to be available at the destination'),
1500 _(b'REV'),
1500 _(b'REV'),
1501 ),
1501 ),
1502 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1502 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1503 (
1503 (
1504 b't',
1504 b't',
1505 b'type',
1505 b'type',
1506 b'bzip2',
1506 b'bzip2',
1507 _(b'bundle compression type to use'),
1507 _(b'bundle compression type to use'),
1508 _(b'TYPE'),
1508 _(b'TYPE'),
1509 ),
1509 ),
1510 ]
1510 ]
1511 + remoteopts,
1511 + remoteopts,
1512 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1512 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1513 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1513 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1514 )
1514 )
1515 def bundle(ui, repo, fname, dest=None, **opts):
1515 def bundle(ui, repo, fname, dest=None, **opts):
1516 """create a bundle file
1516 """create a bundle file
1517
1517
1518 Generate a bundle file containing data to be transferred to another
1518 Generate a bundle file containing data to be transferred to another
1519 repository.
1519 repository.
1520
1520
1521 To create a bundle containing all changesets, use -a/--all
1521 To create a bundle containing all changesets, use -a/--all
1522 (or --base null). Otherwise, hg assumes the destination will have
1522 (or --base null). Otherwise, hg assumes the destination will have
1523 all the nodes you specify with --base parameters. Otherwise, hg
1523 all the nodes you specify with --base parameters. Otherwise, hg
1524 will assume the repository has all the nodes in destination, or
1524 will assume the repository has all the nodes in destination, or
1525 default-push/default if no destination is specified, where destination
1525 default-push/default if no destination is specified, where destination
1526 is the repository you provide through DEST option.
1526 is the repository you provide through DEST option.
1527
1527
1528 You can change bundle format with the -t/--type option. See
1528 You can change bundle format with the -t/--type option. See
1529 :hg:`help bundlespec` for documentation on this format. By default,
1529 :hg:`help bundlespec` for documentation on this format. By default,
1530 the most appropriate format is used and compression defaults to
1530 the most appropriate format is used and compression defaults to
1531 bzip2.
1531 bzip2.
1532
1532
1533 The bundle file can then be transferred using conventional means
1533 The bundle file can then be transferred using conventional means
1534 and applied to another repository with the unbundle or pull
1534 and applied to another repository with the unbundle or pull
1535 command. This is useful when direct push and pull are not
1535 command. This is useful when direct push and pull are not
1536 available or when exporting an entire repository is undesirable.
1536 available or when exporting an entire repository is undesirable.
1537
1537
1538 Applying bundles preserves all changeset contents including
1538 Applying bundles preserves all changeset contents including
1539 permissions, copy/rename information, and revision history.
1539 permissions, copy/rename information, and revision history.
1540
1540
1541 Returns 0 on success, 1 if no changes found.
1541 Returns 0 on success, 1 if no changes found.
1542 """
1542 """
1543 opts = pycompat.byteskwargs(opts)
1543 opts = pycompat.byteskwargs(opts)
1544 revs = None
1544 revs = None
1545 if b'rev' in opts:
1545 if b'rev' in opts:
1546 revstrings = opts[b'rev']
1546 revstrings = opts[b'rev']
1547 revs = scmutil.revrange(repo, revstrings)
1547 revs = scmutil.revrange(repo, revstrings)
1548 if revstrings and not revs:
1548 if revstrings and not revs:
1549 raise error.Abort(_(b'no commits to bundle'))
1549 raise error.Abort(_(b'no commits to bundle'))
1550
1550
1551 bundletype = opts.get(b'type', b'bzip2').lower()
1551 bundletype = opts.get(b'type', b'bzip2').lower()
1552 try:
1552 try:
1553 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1553 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1554 except error.UnsupportedBundleSpecification as e:
1554 except error.UnsupportedBundleSpecification as e:
1555 raise error.Abort(
1555 raise error.Abort(
1556 pycompat.bytestr(e),
1556 pycompat.bytestr(e),
1557 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1557 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1558 )
1558 )
1559 cgversion = bundlespec.contentopts[b"cg.version"]
1559 cgversion = bundlespec.contentopts[b"cg.version"]
1560
1560
1561 # Packed bundles are a pseudo bundle format for now.
1561 # Packed bundles are a pseudo bundle format for now.
1562 if cgversion == b's1':
1562 if cgversion == b's1':
1563 raise error.Abort(
1563 raise error.Abort(
1564 _(b'packed bundles cannot be produced by "hg bundle"'),
1564 _(b'packed bundles cannot be produced by "hg bundle"'),
1565 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1565 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1566 )
1566 )
1567
1567
1568 if opts.get(b'all'):
1568 if opts.get(b'all'):
1569 if dest:
1569 if dest:
1570 raise error.Abort(
1570 raise error.Abort(
1571 _(b"--all is incompatible with specifying a destination")
1571 _(b"--all is incompatible with specifying a destination")
1572 )
1572 )
1573 if opts.get(b'base'):
1573 if opts.get(b'base'):
1574 ui.warn(_(b"ignoring --base because --all was specified\n"))
1574 ui.warn(_(b"ignoring --base because --all was specified\n"))
1575 base = [nullrev]
1575 base = [nullrev]
1576 else:
1576 else:
1577 base = scmutil.revrange(repo, opts.get(b'base'))
1577 base = scmutil.revrange(repo, opts.get(b'base'))
1578 if cgversion not in changegroup.supportedoutgoingversions(repo):
1578 if cgversion not in changegroup.supportedoutgoingversions(repo):
1579 raise error.Abort(
1579 raise error.Abort(
1580 _(b"repository does not support bundle version %s") % cgversion
1580 _(b"repository does not support bundle version %s") % cgversion
1581 )
1581 )
1582
1582
1583 if base:
1583 if base:
1584 if dest:
1584 if dest:
1585 raise error.Abort(
1585 raise error.Abort(
1586 _(b"--base is incompatible with specifying a destination")
1586 _(b"--base is incompatible with specifying a destination")
1587 )
1587 )
1588 common = [repo[rev].node() for rev in base]
1588 common = [repo[rev].node() for rev in base]
1589 heads = [repo[r].node() for r in revs] if revs else None
1589 heads = [repo[r].node() for r in revs] if revs else None
1590 outgoing = discovery.outgoing(repo, common, heads)
1590 outgoing = discovery.outgoing(repo, common, heads)
1591 else:
1591 else:
1592 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1592 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1593 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1593 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1594 other = hg.peer(repo, opts, dest)
1594 other = hg.peer(repo, opts, dest)
1595 revs = [repo[r].hex() for r in revs]
1595 revs = [repo[r].hex() for r in revs]
1596 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1596 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1597 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1597 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1598 outgoing = discovery.findcommonoutgoing(
1598 outgoing = discovery.findcommonoutgoing(
1599 repo,
1599 repo,
1600 other,
1600 other,
1601 onlyheads=heads,
1601 onlyheads=heads,
1602 force=opts.get(b'force'),
1602 force=opts.get(b'force'),
1603 portable=True,
1603 portable=True,
1604 )
1604 )
1605
1605
1606 if not outgoing.missing:
1606 if not outgoing.missing:
1607 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1607 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1608 return 1
1608 return 1
1609
1609
1610 if cgversion == b'01': # bundle1
1610 if cgversion == b'01': # bundle1
1611 bversion = b'HG10' + bundlespec.wirecompression
1611 bversion = b'HG10' + bundlespec.wirecompression
1612 bcompression = None
1612 bcompression = None
1613 elif cgversion in (b'02', b'03'):
1613 elif cgversion in (b'02', b'03'):
1614 bversion = b'HG20'
1614 bversion = b'HG20'
1615 bcompression = bundlespec.wirecompression
1615 bcompression = bundlespec.wirecompression
1616 else:
1616 else:
1617 raise error.ProgrammingError(
1617 raise error.ProgrammingError(
1618 b'bundle: unexpected changegroup version %s' % cgversion
1618 b'bundle: unexpected changegroup version %s' % cgversion
1619 )
1619 )
1620
1620
1621 # TODO compression options should be derived from bundlespec parsing.
1621 # TODO compression options should be derived from bundlespec parsing.
1622 # This is a temporary hack to allow adjusting bundle compression
1622 # This is a temporary hack to allow adjusting bundle compression
1623 # level without a) formalizing the bundlespec changes to declare it
1623 # level without a) formalizing the bundlespec changes to declare it
1624 # b) introducing a command flag.
1624 # b) introducing a command flag.
1625 compopts = {}
1625 compopts = {}
1626 complevel = ui.configint(
1626 complevel = ui.configint(
1627 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1627 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1628 )
1628 )
1629 if complevel is None:
1629 if complevel is None:
1630 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1630 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1631 if complevel is not None:
1631 if complevel is not None:
1632 compopts[b'level'] = complevel
1632 compopts[b'level'] = complevel
1633
1633
1634 # Allow overriding the bundling of obsmarker in phases through
1634 # Allow overriding the bundling of obsmarker in phases through
1635 # configuration while we don't have a bundle version that include them
1635 # configuration while we don't have a bundle version that include them
1636 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1636 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1637 bundlespec.contentopts[b'obsolescence'] = True
1637 bundlespec.contentopts[b'obsolescence'] = True
1638 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1638 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1639 bundlespec.contentopts[b'phases'] = True
1639 bundlespec.contentopts[b'phases'] = True
1640
1640
1641 bundle2.writenewbundle(
1641 bundle2.writenewbundle(
1642 ui,
1642 ui,
1643 repo,
1643 repo,
1644 b'bundle',
1644 b'bundle',
1645 fname,
1645 fname,
1646 bversion,
1646 bversion,
1647 outgoing,
1647 outgoing,
1648 bundlespec.contentopts,
1648 bundlespec.contentopts,
1649 compression=bcompression,
1649 compression=bcompression,
1650 compopts=compopts,
1650 compopts=compopts,
1651 )
1651 )
1652
1652
1653
1653
1654 @command(
1654 @command(
1655 b'cat',
1655 b'cat',
1656 [
1656 [
1657 (
1657 (
1658 b'o',
1658 b'o',
1659 b'output',
1659 b'output',
1660 b'',
1660 b'',
1661 _(b'print output to file with formatted name'),
1661 _(b'print output to file with formatted name'),
1662 _(b'FORMAT'),
1662 _(b'FORMAT'),
1663 ),
1663 ),
1664 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1664 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1665 (b'', b'decode', None, _(b'apply any matching decode filter')),
1665 (b'', b'decode', None, _(b'apply any matching decode filter')),
1666 ]
1666 ]
1667 + walkopts
1667 + walkopts
1668 + formatteropts,
1668 + formatteropts,
1669 _(b'[OPTION]... FILE...'),
1669 _(b'[OPTION]... FILE...'),
1670 helpcategory=command.CATEGORY_FILE_CONTENTS,
1670 helpcategory=command.CATEGORY_FILE_CONTENTS,
1671 inferrepo=True,
1671 inferrepo=True,
1672 intents={INTENT_READONLY},
1672 intents={INTENT_READONLY},
1673 )
1673 )
1674 def cat(ui, repo, file1, *pats, **opts):
1674 def cat(ui, repo, file1, *pats, **opts):
1675 """output the current or given revision of files
1675 """output the current or given revision of files
1676
1676
1677 Print the specified files as they were at the given revision. If
1677 Print the specified files as they were at the given revision. If
1678 no revision is given, the parent of the working directory is used.
1678 no revision is given, the parent of the working directory is used.
1679
1679
1680 Output may be to a file, in which case the name of the file is
1680 Output may be to a file, in which case the name of the file is
1681 given using a template string. See :hg:`help templates`. In addition
1681 given using a template string. See :hg:`help templates`. In addition
1682 to the common template keywords, the following formatting rules are
1682 to the common template keywords, the following formatting rules are
1683 supported:
1683 supported:
1684
1684
1685 :``%%``: literal "%" character
1685 :``%%``: literal "%" character
1686 :``%s``: basename of file being printed
1686 :``%s``: basename of file being printed
1687 :``%d``: dirname of file being printed, or '.' if in repository root
1687 :``%d``: dirname of file being printed, or '.' if in repository root
1688 :``%p``: root-relative path name of file being printed
1688 :``%p``: root-relative path name of file being printed
1689 :``%H``: changeset hash (40 hexadecimal digits)
1689 :``%H``: changeset hash (40 hexadecimal digits)
1690 :``%R``: changeset revision number
1690 :``%R``: changeset revision number
1691 :``%h``: short-form changeset hash (12 hexadecimal digits)
1691 :``%h``: short-form changeset hash (12 hexadecimal digits)
1692 :``%r``: zero-padded changeset revision number
1692 :``%r``: zero-padded changeset revision number
1693 :``%b``: basename of the exporting repository
1693 :``%b``: basename of the exporting repository
1694 :``\\``: literal "\\" character
1694 :``\\``: literal "\\" character
1695
1695
1696 .. container:: verbose
1696 .. container:: verbose
1697
1697
1698 Template:
1698 Template:
1699
1699
1700 The following keywords are supported in addition to the common template
1700 The following keywords are supported in addition to the common template
1701 keywords and functions. See also :hg:`help templates`.
1701 keywords and functions. See also :hg:`help templates`.
1702
1702
1703 :data: String. File content.
1703 :data: String. File content.
1704 :path: String. Repository-absolute path of the file.
1704 :path: String. Repository-absolute path of the file.
1705
1705
1706 Returns 0 on success.
1706 Returns 0 on success.
1707 """
1707 """
1708 opts = pycompat.byteskwargs(opts)
1708 opts = pycompat.byteskwargs(opts)
1709 rev = opts.get(b'rev')
1709 rev = opts.get(b'rev')
1710 if rev:
1710 if rev:
1711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1712 ctx = scmutil.revsingle(repo, rev)
1712 ctx = scmutil.revsingle(repo, rev)
1713 m = scmutil.match(ctx, (file1,) + pats, opts)
1713 m = scmutil.match(ctx, (file1,) + pats, opts)
1714 fntemplate = opts.pop(b'output', b'')
1714 fntemplate = opts.pop(b'output', b'')
1715 if cmdutil.isstdiofilename(fntemplate):
1715 if cmdutil.isstdiofilename(fntemplate):
1716 fntemplate = b''
1716 fntemplate = b''
1717
1717
1718 if fntemplate:
1718 if fntemplate:
1719 fm = formatter.nullformatter(ui, b'cat', opts)
1719 fm = formatter.nullformatter(ui, b'cat', opts)
1720 else:
1720 else:
1721 ui.pager(b'cat')
1721 ui.pager(b'cat')
1722 fm = ui.formatter(b'cat', opts)
1722 fm = ui.formatter(b'cat', opts)
1723 with fm:
1723 with fm:
1724 return cmdutil.cat(
1724 return cmdutil.cat(
1725 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1725 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1726 )
1726 )
1727
1727
1728
1728
1729 @command(
1729 @command(
1730 b'clone',
1730 b'clone',
1731 [
1731 [
1732 (
1732 (
1733 b'U',
1733 b'U',
1734 b'noupdate',
1734 b'noupdate',
1735 None,
1735 None,
1736 _(
1736 _(
1737 b'the clone will include an empty working '
1737 b'the clone will include an empty working '
1738 b'directory (only a repository)'
1738 b'directory (only a repository)'
1739 ),
1739 ),
1740 ),
1740 ),
1741 (
1741 (
1742 b'u',
1742 b'u',
1743 b'updaterev',
1743 b'updaterev',
1744 b'',
1744 b'',
1745 _(b'revision, tag, or branch to check out'),
1745 _(b'revision, tag, or branch to check out'),
1746 _(b'REV'),
1746 _(b'REV'),
1747 ),
1747 ),
1748 (
1748 (
1749 b'r',
1749 b'r',
1750 b'rev',
1750 b'rev',
1751 [],
1751 [],
1752 _(
1752 _(
1753 b'do not clone everything, but include this changeset'
1753 b'do not clone everything, but include this changeset'
1754 b' and its ancestors'
1754 b' and its ancestors'
1755 ),
1755 ),
1756 _(b'REV'),
1756 _(b'REV'),
1757 ),
1757 ),
1758 (
1758 (
1759 b'b',
1759 b'b',
1760 b'branch',
1760 b'branch',
1761 [],
1761 [],
1762 _(
1762 _(
1763 b'do not clone everything, but include this branch\'s'
1763 b'do not clone everything, but include this branch\'s'
1764 b' changesets and their ancestors'
1764 b' changesets and their ancestors'
1765 ),
1765 ),
1766 _(b'BRANCH'),
1766 _(b'BRANCH'),
1767 ),
1767 ),
1768 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1768 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1769 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1769 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1770 (b'', b'stream', None, _(b'clone with minimal data processing')),
1770 (b'', b'stream', None, _(b'clone with minimal data processing')),
1771 ]
1771 ]
1772 + remoteopts,
1772 + remoteopts,
1773 _(b'[OPTION]... SOURCE [DEST]'),
1773 _(b'[OPTION]... SOURCE [DEST]'),
1774 helpcategory=command.CATEGORY_REPO_CREATION,
1774 helpcategory=command.CATEGORY_REPO_CREATION,
1775 helpbasic=True,
1775 helpbasic=True,
1776 norepo=True,
1776 norepo=True,
1777 )
1777 )
1778 def clone(ui, source, dest=None, **opts):
1778 def clone(ui, source, dest=None, **opts):
1779 """make a copy of an existing repository
1779 """make a copy of an existing repository
1780
1780
1781 Create a copy of an existing repository in a new directory.
1781 Create a copy of an existing repository in a new directory.
1782
1782
1783 If no destination directory name is specified, it defaults to the
1783 If no destination directory name is specified, it defaults to the
1784 basename of the source.
1784 basename of the source.
1785
1785
1786 The location of the source is added to the new repository's
1786 The location of the source is added to the new repository's
1787 ``.hg/hgrc`` file, as the default to be used for future pulls.
1787 ``.hg/hgrc`` file, as the default to be used for future pulls.
1788
1788
1789 Only local paths and ``ssh://`` URLs are supported as
1789 Only local paths and ``ssh://`` URLs are supported as
1790 destinations. For ``ssh://`` destinations, no working directory or
1790 destinations. For ``ssh://`` destinations, no working directory or
1791 ``.hg/hgrc`` will be created on the remote side.
1791 ``.hg/hgrc`` will be created on the remote side.
1792
1792
1793 If the source repository has a bookmark called '@' set, that
1793 If the source repository has a bookmark called '@' set, that
1794 revision will be checked out in the new repository by default.
1794 revision will be checked out in the new repository by default.
1795
1795
1796 To check out a particular version, use -u/--update, or
1796 To check out a particular version, use -u/--update, or
1797 -U/--noupdate to create a clone with no working directory.
1797 -U/--noupdate to create a clone with no working directory.
1798
1798
1799 To pull only a subset of changesets, specify one or more revisions
1799 To pull only a subset of changesets, specify one or more revisions
1800 identifiers with -r/--rev or branches with -b/--branch. The
1800 identifiers with -r/--rev or branches with -b/--branch. The
1801 resulting clone will contain only the specified changesets and
1801 resulting clone will contain only the specified changesets and
1802 their ancestors. These options (or 'clone src#rev dest') imply
1802 their ancestors. These options (or 'clone src#rev dest') imply
1803 --pull, even for local source repositories.
1803 --pull, even for local source repositories.
1804
1804
1805 In normal clone mode, the remote normalizes repository data into a common
1805 In normal clone mode, the remote normalizes repository data into a common
1806 exchange format and the receiving end translates this data into its local
1806 exchange format and the receiving end translates this data into its local
1807 storage format. --stream activates a different clone mode that essentially
1807 storage format. --stream activates a different clone mode that essentially
1808 copies repository files from the remote with minimal data processing. This
1808 copies repository files from the remote with minimal data processing. This
1809 significantly reduces the CPU cost of a clone both remotely and locally.
1809 significantly reduces the CPU cost of a clone both remotely and locally.
1810 However, it often increases the transferred data size by 30-40%. This can
1810 However, it often increases the transferred data size by 30-40%. This can
1811 result in substantially faster clones where I/O throughput is plentiful,
1811 result in substantially faster clones where I/O throughput is plentiful,
1812 especially for larger repositories. A side-effect of --stream clones is
1812 especially for larger repositories. A side-effect of --stream clones is
1813 that storage settings and requirements on the remote are applied locally:
1813 that storage settings and requirements on the remote are applied locally:
1814 a modern client may inherit legacy or inefficient storage used by the
1814 a modern client may inherit legacy or inefficient storage used by the
1815 remote or a legacy Mercurial client may not be able to clone from a
1815 remote or a legacy Mercurial client may not be able to clone from a
1816 modern Mercurial remote.
1816 modern Mercurial remote.
1817
1817
1818 .. note::
1818 .. note::
1819
1819
1820 Specifying a tag will include the tagged changeset but not the
1820 Specifying a tag will include the tagged changeset but not the
1821 changeset containing the tag.
1821 changeset containing the tag.
1822
1822
1823 .. container:: verbose
1823 .. container:: verbose
1824
1824
1825 For efficiency, hardlinks are used for cloning whenever the
1825 For efficiency, hardlinks are used for cloning whenever the
1826 source and destination are on the same filesystem (note this
1826 source and destination are on the same filesystem (note this
1827 applies only to the repository data, not to the working
1827 applies only to the repository data, not to the working
1828 directory). Some filesystems, such as AFS, implement hardlinking
1828 directory). Some filesystems, such as AFS, implement hardlinking
1829 incorrectly, but do not report errors. In these cases, use the
1829 incorrectly, but do not report errors. In these cases, use the
1830 --pull option to avoid hardlinking.
1830 --pull option to avoid hardlinking.
1831
1831
1832 Mercurial will update the working directory to the first applicable
1832 Mercurial will update the working directory to the first applicable
1833 revision from this list:
1833 revision from this list:
1834
1834
1835 a) null if -U or the source repository has no changesets
1835 a) null if -U or the source repository has no changesets
1836 b) if -u . and the source repository is local, the first parent of
1836 b) if -u . and the source repository is local, the first parent of
1837 the source repository's working directory
1837 the source repository's working directory
1838 c) the changeset specified with -u (if a branch name, this means the
1838 c) the changeset specified with -u (if a branch name, this means the
1839 latest head of that branch)
1839 latest head of that branch)
1840 d) the changeset specified with -r
1840 d) the changeset specified with -r
1841 e) the tipmost head specified with -b
1841 e) the tipmost head specified with -b
1842 f) the tipmost head specified with the url#branch source syntax
1842 f) the tipmost head specified with the url#branch source syntax
1843 g) the revision marked with the '@' bookmark, if present
1843 g) the revision marked with the '@' bookmark, if present
1844 h) the tipmost head of the default branch
1844 h) the tipmost head of the default branch
1845 i) tip
1845 i) tip
1846
1846
1847 When cloning from servers that support it, Mercurial may fetch
1847 When cloning from servers that support it, Mercurial may fetch
1848 pre-generated data from a server-advertised URL or inline from the
1848 pre-generated data from a server-advertised URL or inline from the
1849 same stream. When this is done, hooks operating on incoming changesets
1849 same stream. When this is done, hooks operating on incoming changesets
1850 and changegroups may fire more than once, once for each pre-generated
1850 and changegroups may fire more than once, once for each pre-generated
1851 bundle and as well as for any additional remaining data. In addition,
1851 bundle and as well as for any additional remaining data. In addition,
1852 if an error occurs, the repository may be rolled back to a partial
1852 if an error occurs, the repository may be rolled back to a partial
1853 clone. This behavior may change in future releases.
1853 clone. This behavior may change in future releases.
1854 See :hg:`help -e clonebundles` for more.
1854 See :hg:`help -e clonebundles` for more.
1855
1855
1856 Examples:
1856 Examples:
1857
1857
1858 - clone a remote repository to a new directory named hg/::
1858 - clone a remote repository to a new directory named hg/::
1859
1859
1860 hg clone https://www.mercurial-scm.org/repo/hg/
1860 hg clone https://www.mercurial-scm.org/repo/hg/
1861
1861
1862 - create a lightweight local clone::
1862 - create a lightweight local clone::
1863
1863
1864 hg clone project/ project-feature/
1864 hg clone project/ project-feature/
1865
1865
1866 - clone from an absolute path on an ssh server (note double-slash)::
1866 - clone from an absolute path on an ssh server (note double-slash)::
1867
1867
1868 hg clone ssh://user@server//home/projects/alpha/
1868 hg clone ssh://user@server//home/projects/alpha/
1869
1869
1870 - do a streaming clone while checking out a specified version::
1870 - do a streaming clone while checking out a specified version::
1871
1871
1872 hg clone --stream http://server/repo -u 1.5
1872 hg clone --stream http://server/repo -u 1.5
1873
1873
1874 - create a repository without changesets after a particular revision::
1874 - create a repository without changesets after a particular revision::
1875
1875
1876 hg clone -r 04e544 experimental/ good/
1876 hg clone -r 04e544 experimental/ good/
1877
1877
1878 - clone (and track) a particular named branch::
1878 - clone (and track) a particular named branch::
1879
1879
1880 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1880 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1881
1881
1882 See :hg:`help urls` for details on specifying URLs.
1882 See :hg:`help urls` for details on specifying URLs.
1883
1883
1884 Returns 0 on success.
1884 Returns 0 on success.
1885 """
1885 """
1886 opts = pycompat.byteskwargs(opts)
1886 opts = pycompat.byteskwargs(opts)
1887 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1887 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1888
1888
1889 # --include/--exclude can come from narrow or sparse.
1889 # --include/--exclude can come from narrow or sparse.
1890 includepats, excludepats = None, None
1890 includepats, excludepats = None, None
1891
1891
1892 # hg.clone() differentiates between None and an empty set. So make sure
1892 # hg.clone() differentiates between None and an empty set. So make sure
1893 # patterns are sets if narrow is requested without patterns.
1893 # patterns are sets if narrow is requested without patterns.
1894 if opts.get(b'narrow'):
1894 if opts.get(b'narrow'):
1895 includepats = set()
1895 includepats = set()
1896 excludepats = set()
1896 excludepats = set()
1897
1897
1898 if opts.get(b'include'):
1898 if opts.get(b'include'):
1899 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1899 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1900 if opts.get(b'exclude'):
1900 if opts.get(b'exclude'):
1901 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1901 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1902
1902
1903 r = hg.clone(
1903 r = hg.clone(
1904 ui,
1904 ui,
1905 opts,
1905 opts,
1906 source,
1906 source,
1907 dest,
1907 dest,
1908 pull=opts.get(b'pull'),
1908 pull=opts.get(b'pull'),
1909 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1909 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1910 revs=opts.get(b'rev'),
1910 revs=opts.get(b'rev'),
1911 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1911 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1912 branch=opts.get(b'branch'),
1912 branch=opts.get(b'branch'),
1913 shareopts=opts.get(b'shareopts'),
1913 shareopts=opts.get(b'shareopts'),
1914 storeincludepats=includepats,
1914 storeincludepats=includepats,
1915 storeexcludepats=excludepats,
1915 storeexcludepats=excludepats,
1916 depth=opts.get(b'depth') or None,
1916 depth=opts.get(b'depth') or None,
1917 )
1917 )
1918
1918
1919 return r is None
1919 return r is None
1920
1920
1921
1921
1922 @command(
1922 @command(
1923 b'commit|ci',
1923 b'commit|ci',
1924 [
1924 [
1925 (
1925 (
1926 b'A',
1926 b'A',
1927 b'addremove',
1927 b'addremove',
1928 None,
1928 None,
1929 _(b'mark new/missing files as added/removed before committing'),
1929 _(b'mark new/missing files as added/removed before committing'),
1930 ),
1930 ),
1931 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1931 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1932 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1932 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1933 (b's', b'secret', None, _(b'use the secret phase for committing')),
1933 (b's', b'secret', None, _(b'use the secret phase for committing')),
1934 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1934 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1935 (
1935 (
1936 b'',
1936 b'',
1937 b'force-close-branch',
1937 b'force-close-branch',
1938 None,
1938 None,
1939 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1939 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1940 ),
1940 ),
1941 (b'i', b'interactive', None, _(b'use interactive mode')),
1941 (b'i', b'interactive', None, _(b'use interactive mode')),
1942 ]
1942 ]
1943 + walkopts
1943 + walkopts
1944 + commitopts
1944 + commitopts
1945 + commitopts2
1945 + commitopts2
1946 + subrepoopts,
1946 + subrepoopts,
1947 _(b'[OPTION]... [FILE]...'),
1947 _(b'[OPTION]... [FILE]...'),
1948 helpcategory=command.CATEGORY_COMMITTING,
1948 helpcategory=command.CATEGORY_COMMITTING,
1949 helpbasic=True,
1949 helpbasic=True,
1950 inferrepo=True,
1950 inferrepo=True,
1951 )
1951 )
1952 def commit(ui, repo, *pats, **opts):
1952 def commit(ui, repo, *pats, **opts):
1953 """commit the specified files or all outstanding changes
1953 """commit the specified files or all outstanding changes
1954
1954
1955 Commit changes to the given files into the repository. Unlike a
1955 Commit changes to the given files into the repository. Unlike a
1956 centralized SCM, this operation is a local operation. See
1956 centralized SCM, this operation is a local operation. See
1957 :hg:`push` for a way to actively distribute your changes.
1957 :hg:`push` for a way to actively distribute your changes.
1958
1958
1959 If a list of files is omitted, all changes reported by :hg:`status`
1959 If a list of files is omitted, all changes reported by :hg:`status`
1960 will be committed.
1960 will be committed.
1961
1961
1962 If you are committing the result of a merge, do not provide any
1962 If you are committing the result of a merge, do not provide any
1963 filenames or -I/-X filters.
1963 filenames or -I/-X filters.
1964
1964
1965 If no commit message is specified, Mercurial starts your
1965 If no commit message is specified, Mercurial starts your
1966 configured editor where you can enter a message. In case your
1966 configured editor where you can enter a message. In case your
1967 commit fails, you will find a backup of your message in
1967 commit fails, you will find a backup of your message in
1968 ``.hg/last-message.txt``.
1968 ``.hg/last-message.txt``.
1969
1969
1970 The --close-branch flag can be used to mark the current branch
1970 The --close-branch flag can be used to mark the current branch
1971 head closed. When all heads of a branch are closed, the branch
1971 head closed. When all heads of a branch are closed, the branch
1972 will be considered closed and no longer listed.
1972 will be considered closed and no longer listed.
1973
1973
1974 The --amend flag can be used to amend the parent of the
1974 The --amend flag can be used to amend the parent of the
1975 working directory with a new commit that contains the changes
1975 working directory with a new commit that contains the changes
1976 in the parent in addition to those currently reported by :hg:`status`,
1976 in the parent in addition to those currently reported by :hg:`status`,
1977 if there are any. The old commit is stored in a backup bundle in
1977 if there are any. The old commit is stored in a backup bundle in
1978 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1978 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1979 on how to restore it).
1979 on how to restore it).
1980
1980
1981 Message, user and date are taken from the amended commit unless
1981 Message, user and date are taken from the amended commit unless
1982 specified. When a message isn't specified on the command line,
1982 specified. When a message isn't specified on the command line,
1983 the editor will open with the message of the amended commit.
1983 the editor will open with the message of the amended commit.
1984
1984
1985 It is not possible to amend public changesets (see :hg:`help phases`)
1985 It is not possible to amend public changesets (see :hg:`help phases`)
1986 or changesets that have children.
1986 or changesets that have children.
1987
1987
1988 See :hg:`help dates` for a list of formats valid for -d/--date.
1988 See :hg:`help dates` for a list of formats valid for -d/--date.
1989
1989
1990 Returns 0 on success, 1 if nothing changed.
1990 Returns 0 on success, 1 if nothing changed.
1991
1991
1992 .. container:: verbose
1992 .. container:: verbose
1993
1993
1994 Examples:
1994 Examples:
1995
1995
1996 - commit all files ending in .py::
1996 - commit all files ending in .py::
1997
1997
1998 hg commit --include "set:**.py"
1998 hg commit --include "set:**.py"
1999
1999
2000 - commit all non-binary files::
2000 - commit all non-binary files::
2001
2001
2002 hg commit --exclude "set:binary()"
2002 hg commit --exclude "set:binary()"
2003
2003
2004 - amend the current commit and set the date to now::
2004 - amend the current commit and set the date to now::
2005
2005
2006 hg commit --amend --date now
2006 hg commit --amend --date now
2007 """
2007 """
2008 with repo.wlock(), repo.lock():
2008 with repo.wlock(), repo.lock():
2009 return _docommit(ui, repo, *pats, **opts)
2009 return _docommit(ui, repo, *pats, **opts)
2010
2010
2011
2011
2012 def _docommit(ui, repo, *pats, **opts):
2012 def _docommit(ui, repo, *pats, **opts):
2013 if opts.get('interactive'):
2013 if opts.get('interactive'):
2014 opts.pop('interactive')
2014 opts.pop('interactive')
2015 ret = cmdutil.dorecord(
2015 ret = cmdutil.dorecord(
2016 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2016 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2017 )
2017 )
2018 # ret can be 0 (no changes to record) or the value returned by
2018 # ret can be 0 (no changes to record) or the value returned by
2019 # commit(), 1 if nothing changed or None on success.
2019 # commit(), 1 if nothing changed or None on success.
2020 return 1 if ret == 0 else ret
2020 return 1 if ret == 0 else ret
2021
2021
2022 opts = pycompat.byteskwargs(opts)
2022 opts = pycompat.byteskwargs(opts)
2023 if opts.get(b'subrepos'):
2023 if opts.get(b'subrepos'):
2024 if opts.get(b'amend'):
2024 if opts.get(b'amend'):
2025 raise error.Abort(_(b'cannot amend with --subrepos'))
2025 raise error.Abort(_(b'cannot amend with --subrepos'))
2026 # Let --subrepos on the command line override config setting.
2026 # Let --subrepos on the command line override config setting.
2027 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2027 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2028
2028
2029 cmdutil.checkunfinished(repo, commit=True)
2029 cmdutil.checkunfinished(repo, commit=True)
2030
2030
2031 branch = repo[None].branch()
2031 branch = repo[None].branch()
2032 bheads = repo.branchheads(branch)
2032 bheads = repo.branchheads(branch)
2033
2033
2034 extra = {}
2034 extra = {}
2035 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2035 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2036 extra[b'close'] = b'1'
2036 extra[b'close'] = b'1'
2037
2037
2038 if repo[b'.'].closesbranch():
2038 if repo[b'.'].closesbranch():
2039 raise error.Abort(
2039 raise error.Abort(
2040 _(b'current revision is already a branch closing head')
2040 _(b'current revision is already a branch closing head')
2041 )
2041 )
2042 elif not bheads:
2042 elif not bheads:
2043 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2043 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2044 elif (
2044 elif (
2045 branch == repo[b'.'].branch()
2045 branch == repo[b'.'].branch()
2046 and repo[b'.'].node() not in bheads
2046 and repo[b'.'].node() not in bheads
2047 and not opts.get(b'force_close_branch')
2047 and not opts.get(b'force_close_branch')
2048 ):
2048 ):
2049 hint = _(
2049 hint = _(
2050 b'use --force-close-branch to close branch from a non-head'
2050 b'use --force-close-branch to close branch from a non-head'
2051 b' changeset'
2051 b' changeset'
2052 )
2052 )
2053 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2053 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2054 elif opts.get(b'amend'):
2054 elif opts.get(b'amend'):
2055 if (
2055 if (
2056 repo[b'.'].p1().branch() != branch
2056 repo[b'.'].p1().branch() != branch
2057 and repo[b'.'].p2().branch() != branch
2057 and repo[b'.'].p2().branch() != branch
2058 ):
2058 ):
2059 raise error.Abort(_(b'can only close branch heads'))
2059 raise error.Abort(_(b'can only close branch heads'))
2060
2060
2061 if opts.get(b'amend'):
2061 if opts.get(b'amend'):
2062 if ui.configbool(b'ui', b'commitsubrepos'):
2062 if ui.configbool(b'ui', b'commitsubrepos'):
2063 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2063 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2064
2064
2065 old = repo[b'.']
2065 old = repo[b'.']
2066 rewriteutil.precheck(repo, [old.rev()], b'amend')
2066 rewriteutil.precheck(repo, [old.rev()], b'amend')
2067
2067
2068 # Currently histedit gets confused if an amend happens while histedit
2068 # Currently histedit gets confused if an amend happens while histedit
2069 # is in progress. Since we have a checkunfinished command, we are
2069 # is in progress. Since we have a checkunfinished command, we are
2070 # temporarily honoring it.
2070 # temporarily honoring it.
2071 #
2071 #
2072 # Note: eventually this guard will be removed. Please do not expect
2072 # Note: eventually this guard will be removed. Please do not expect
2073 # this behavior to remain.
2073 # this behavior to remain.
2074 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2074 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2075 cmdutil.checkunfinished(repo)
2075 cmdutil.checkunfinished(repo)
2076
2076
2077 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2077 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2078 if node == old.node():
2078 if node == old.node():
2079 ui.status(_(b"nothing changed\n"))
2079 ui.status(_(b"nothing changed\n"))
2080 return 1
2080 return 1
2081 else:
2081 else:
2082
2082
2083 def commitfunc(ui, repo, message, match, opts):
2083 def commitfunc(ui, repo, message, match, opts):
2084 overrides = {}
2084 overrides = {}
2085 if opts.get(b'secret'):
2085 if opts.get(b'secret'):
2086 overrides[(b'phases', b'new-commit')] = b'secret'
2086 overrides[(b'phases', b'new-commit')] = b'secret'
2087
2087
2088 baseui = repo.baseui
2088 baseui = repo.baseui
2089 with baseui.configoverride(overrides, b'commit'):
2089 with baseui.configoverride(overrides, b'commit'):
2090 with ui.configoverride(overrides, b'commit'):
2090 with ui.configoverride(overrides, b'commit'):
2091 editform = cmdutil.mergeeditform(
2091 editform = cmdutil.mergeeditform(
2092 repo[None], b'commit.normal'
2092 repo[None], b'commit.normal'
2093 )
2093 )
2094 editor = cmdutil.getcommiteditor(
2094 editor = cmdutil.getcommiteditor(
2095 editform=editform, **pycompat.strkwargs(opts)
2095 editform=editform, **pycompat.strkwargs(opts)
2096 )
2096 )
2097 return repo.commit(
2097 return repo.commit(
2098 message,
2098 message,
2099 opts.get(b'user'),
2099 opts.get(b'user'),
2100 opts.get(b'date'),
2100 opts.get(b'date'),
2101 match,
2101 match,
2102 editor=editor,
2102 editor=editor,
2103 extra=extra,
2103 extra=extra,
2104 )
2104 )
2105
2105
2106 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2106 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2107
2107
2108 if not node:
2108 if not node:
2109 stat = cmdutil.postcommitstatus(repo, pats, opts)
2109 stat = cmdutil.postcommitstatus(repo, pats, opts)
2110 if stat.deleted:
2110 if stat.deleted:
2111 ui.status(
2111 ui.status(
2112 _(
2112 _(
2113 b"nothing changed (%d missing files, see "
2113 b"nothing changed (%d missing files, see "
2114 b"'hg status')\n"
2114 b"'hg status')\n"
2115 )
2115 )
2116 % len(stat.deleted)
2116 % len(stat.deleted)
2117 )
2117 )
2118 else:
2118 else:
2119 ui.status(_(b"nothing changed\n"))
2119 ui.status(_(b"nothing changed\n"))
2120 return 1
2120 return 1
2121
2121
2122 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2122 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2123
2123
2124 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2124 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2125 status(
2125 status(
2126 ui,
2126 ui,
2127 repo,
2127 repo,
2128 modified=True,
2128 modified=True,
2129 added=True,
2129 added=True,
2130 removed=True,
2130 removed=True,
2131 deleted=True,
2131 deleted=True,
2132 unknown=True,
2132 unknown=True,
2133 subrepos=opts.get(b'subrepos'),
2133 subrepos=opts.get(b'subrepos'),
2134 )
2134 )
2135
2135
2136
2136
2137 @command(
2137 @command(
2138 b'config|showconfig|debugconfig',
2138 b'config|showconfig|debugconfig',
2139 [
2139 [
2140 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2140 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2141 (b'e', b'edit', None, _(b'edit user config')),
2141 (b'e', b'edit', None, _(b'edit user config')),
2142 (b'l', b'local', None, _(b'edit repository config')),
2142 (b'l', b'local', None, _(b'edit repository config')),
2143 (b'g', b'global', None, _(b'edit global config')),
2143 (b'g', b'global', None, _(b'edit global config')),
2144 ]
2144 ]
2145 + formatteropts,
2145 + formatteropts,
2146 _(b'[-u] [NAME]...'),
2146 _(b'[-u] [NAME]...'),
2147 helpcategory=command.CATEGORY_HELP,
2147 helpcategory=command.CATEGORY_HELP,
2148 optionalrepo=True,
2148 optionalrepo=True,
2149 intents={INTENT_READONLY},
2149 intents={INTENT_READONLY},
2150 )
2150 )
2151 def config(ui, repo, *values, **opts):
2151 def config(ui, repo, *values, **opts):
2152 """show combined config settings from all hgrc files
2152 """show combined config settings from all hgrc files
2153
2153
2154 With no arguments, print names and values of all config items.
2154 With no arguments, print names and values of all config items.
2155
2155
2156 With one argument of the form section.name, print just the value
2156 With one argument of the form section.name, print just the value
2157 of that config item.
2157 of that config item.
2158
2158
2159 With multiple arguments, print names and values of all config
2159 With multiple arguments, print names and values of all config
2160 items with matching section names or section.names.
2160 items with matching section names or section.names.
2161
2161
2162 With --edit, start an editor on the user-level config file. With
2162 With --edit, start an editor on the user-level config file. With
2163 --global, edit the system-wide config file. With --local, edit the
2163 --global, edit the system-wide config file. With --local, edit the
2164 repository-level config file.
2164 repository-level config file.
2165
2165
2166 With --debug, the source (filename and line number) is printed
2166 With --debug, the source (filename and line number) is printed
2167 for each config item.
2167 for each config item.
2168
2168
2169 See :hg:`help config` for more information about config files.
2169 See :hg:`help config` for more information about config files.
2170
2170
2171 .. container:: verbose
2171 .. container:: verbose
2172
2172
2173 Template:
2173 Template:
2174
2174
2175 The following keywords are supported. See also :hg:`help templates`.
2175 The following keywords are supported. See also :hg:`help templates`.
2176
2176
2177 :name: String. Config name.
2177 :name: String. Config name.
2178 :source: String. Filename and line number where the item is defined.
2178 :source: String. Filename and line number where the item is defined.
2179 :value: String. Config value.
2179 :value: String. Config value.
2180
2180
2181 Returns 0 on success, 1 if NAME does not exist.
2181 Returns 0 on success, 1 if NAME does not exist.
2182
2182
2183 """
2183 """
2184
2184
2185 opts = pycompat.byteskwargs(opts)
2185 opts = pycompat.byteskwargs(opts)
2186 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2186 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2187 if opts.get(b'local') and opts.get(b'global'):
2187 if opts.get(b'local') and opts.get(b'global'):
2188 raise error.Abort(_(b"can't use --local and --global together"))
2188 raise error.Abort(_(b"can't use --local and --global together"))
2189
2189
2190 if opts.get(b'local'):
2190 if opts.get(b'local'):
2191 if not repo:
2191 if not repo:
2192 raise error.Abort(_(b"can't use --local outside a repository"))
2192 raise error.Abort(_(b"can't use --local outside a repository"))
2193 paths = [repo.vfs.join(b'hgrc')]
2193 paths = [repo.vfs.join(b'hgrc')]
2194 elif opts.get(b'global'):
2194 elif opts.get(b'global'):
2195 paths = rcutil.systemrcpath()
2195 paths = rcutil.systemrcpath()
2196 else:
2196 else:
2197 paths = rcutil.userrcpath()
2197 paths = rcutil.userrcpath()
2198
2198
2199 for f in paths:
2199 for f in paths:
2200 if os.path.exists(f):
2200 if os.path.exists(f):
2201 break
2201 break
2202 else:
2202 else:
2203 if opts.get(b'global'):
2203 if opts.get(b'global'):
2204 samplehgrc = uimod.samplehgrcs[b'global']
2204 samplehgrc = uimod.samplehgrcs[b'global']
2205 elif opts.get(b'local'):
2205 elif opts.get(b'local'):
2206 samplehgrc = uimod.samplehgrcs[b'local']
2206 samplehgrc = uimod.samplehgrcs[b'local']
2207 else:
2207 else:
2208 samplehgrc = uimod.samplehgrcs[b'user']
2208 samplehgrc = uimod.samplehgrcs[b'user']
2209
2209
2210 f = paths[0]
2210 f = paths[0]
2211 fp = open(f, b"wb")
2211 fp = open(f, b"wb")
2212 fp.write(util.tonativeeol(samplehgrc))
2212 fp.write(util.tonativeeol(samplehgrc))
2213 fp.close()
2213 fp.close()
2214
2214
2215 editor = ui.geteditor()
2215 editor = ui.geteditor()
2216 ui.system(
2216 ui.system(
2217 b"%s \"%s\"" % (editor, f),
2217 b"%s \"%s\"" % (editor, f),
2218 onerr=error.Abort,
2218 onerr=error.Abort,
2219 errprefix=_(b"edit failed"),
2219 errprefix=_(b"edit failed"),
2220 blockedtag=b'config_edit',
2220 blockedtag=b'config_edit',
2221 )
2221 )
2222 return
2222 return
2223 ui.pager(b'config')
2223 ui.pager(b'config')
2224 fm = ui.formatter(b'config', opts)
2224 fm = ui.formatter(b'config', opts)
2225 for t, f in rcutil.rccomponents():
2225 for t, f in rcutil.rccomponents():
2226 if t == b'path':
2226 if t == b'path':
2227 ui.debug(b'read config from: %s\n' % f)
2227 ui.debug(b'read config from: %s\n' % f)
2228 elif t == b'resource':
2228 elif t == b'resource':
2229 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2229 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2230 elif t == b'items':
2230 elif t == b'items':
2231 # Don't print anything for 'items'.
2231 # Don't print anything for 'items'.
2232 pass
2232 pass
2233 else:
2233 else:
2234 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2234 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2235 untrusted = bool(opts.get(b'untrusted'))
2235 untrusted = bool(opts.get(b'untrusted'))
2236
2236
2237 selsections = selentries = []
2237 selsections = selentries = []
2238 if values:
2238 if values:
2239 selsections = [v for v in values if b'.' not in v]
2239 selsections = [v for v in values if b'.' not in v]
2240 selentries = [v for v in values if b'.' in v]
2240 selentries = [v for v in values if b'.' in v]
2241 uniquesel = len(selentries) == 1 and not selsections
2241 uniquesel = len(selentries) == 1 and not selsections
2242 selsections = set(selsections)
2242 selsections = set(selsections)
2243 selentries = set(selentries)
2243 selentries = set(selentries)
2244
2244
2245 matched = False
2245 matched = False
2246 for section, name, value in ui.walkconfig(untrusted=untrusted):
2246 for section, name, value in ui.walkconfig(untrusted=untrusted):
2247 source = ui.configsource(section, name, untrusted)
2247 source = ui.configsource(section, name, untrusted)
2248 value = pycompat.bytestr(value)
2248 value = pycompat.bytestr(value)
2249 defaultvalue = ui.configdefault(section, name)
2249 defaultvalue = ui.configdefault(section, name)
2250 if fm.isplain():
2250 if fm.isplain():
2251 source = source or b'none'
2251 source = source or b'none'
2252 value = value.replace(b'\n', b'\\n')
2252 value = value.replace(b'\n', b'\\n')
2253 entryname = section + b'.' + name
2253 entryname = section + b'.' + name
2254 if values and not (section in selsections or entryname in selentries):
2254 if values and not (section in selsections or entryname in selentries):
2255 continue
2255 continue
2256 fm.startitem()
2256 fm.startitem()
2257 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2257 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2258 if uniquesel:
2258 if uniquesel:
2259 fm.data(name=entryname)
2259 fm.data(name=entryname)
2260 fm.write(b'value', b'%s\n', value)
2260 fm.write(b'value', b'%s\n', value)
2261 else:
2261 else:
2262 fm.write(b'name value', b'%s=%s\n', entryname, value)
2262 fm.write(b'name value', b'%s=%s\n', entryname, value)
2263 if formatter.isprintable(defaultvalue):
2263 if formatter.isprintable(defaultvalue):
2264 fm.data(defaultvalue=defaultvalue)
2264 fm.data(defaultvalue=defaultvalue)
2265 elif isinstance(defaultvalue, list) and all(
2265 elif isinstance(defaultvalue, list) and all(
2266 formatter.isprintable(e) for e in defaultvalue
2266 formatter.isprintable(e) for e in defaultvalue
2267 ):
2267 ):
2268 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2268 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2269 # TODO: no idea how to process unsupported defaultvalue types
2269 # TODO: no idea how to process unsupported defaultvalue types
2270 matched = True
2270 matched = True
2271 fm.end()
2271 fm.end()
2272 if matched:
2272 if matched:
2273 return 0
2273 return 0
2274 return 1
2274 return 1
2275
2275
2276
2276
2277 @command(
2277 @command(
2278 b'continue',
2278 b'continue',
2279 dryrunopts,
2279 dryrunopts,
2280 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2280 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2281 helpbasic=True,
2281 helpbasic=True,
2282 )
2282 )
2283 def continuecmd(ui, repo, **opts):
2283 def continuecmd(ui, repo, **opts):
2284 """resumes an interrupted operation (EXPERIMENTAL)
2284 """resumes an interrupted operation (EXPERIMENTAL)
2285
2285
2286 Finishes a multistep operation like graft, histedit, rebase, merge,
2286 Finishes a multistep operation like graft, histedit, rebase, merge,
2287 and unshelve if they are in an interrupted state.
2287 and unshelve if they are in an interrupted state.
2288
2288
2289 use --dry-run/-n to dry run the command.
2289 use --dry-run/-n to dry run the command.
2290 """
2290 """
2291 dryrun = opts.get('dry_run')
2291 dryrun = opts.get('dry_run')
2292 contstate = cmdutil.getunfinishedstate(repo)
2292 contstate = cmdutil.getunfinishedstate(repo)
2293 if not contstate:
2293 if not contstate:
2294 raise error.Abort(_(b'no operation in progress'))
2294 raise error.Abort(_(b'no operation in progress'))
2295 if not contstate.continuefunc:
2295 if not contstate.continuefunc:
2296 raise error.Abort(
2296 raise error.Abort(
2297 (
2297 (
2298 _(b"%s in progress but does not support 'hg continue'")
2298 _(b"%s in progress but does not support 'hg continue'")
2299 % (contstate._opname)
2299 % (contstate._opname)
2300 ),
2300 ),
2301 hint=contstate.continuemsg(),
2301 hint=contstate.continuemsg(),
2302 )
2302 )
2303 if dryrun:
2303 if dryrun:
2304 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2304 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2305 return
2305 return
2306 return contstate.continuefunc(ui, repo)
2306 return contstate.continuefunc(ui, repo)
2307
2307
2308
2308
2309 @command(
2309 @command(
2310 b'copy|cp',
2310 b'copy|cp',
2311 [
2311 [
2312 (b'', b'forget', None, _(b'unmark a file as copied')),
2312 (b'', b'forget', None, _(b'unmark a file as copied')),
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2314 (
2314 (
2315 b'',
2315 b'',
2316 b'at-rev',
2316 b'at-rev',
2317 b'',
2317 b'',
2318 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2318 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2319 _(b'REV'),
2319 _(b'REV'),
2320 ),
2320 ),
2321 (
2321 (
2322 b'f',
2322 b'f',
2323 b'force',
2323 b'force',
2324 None,
2324 None,
2325 _(b'forcibly copy over an existing managed file'),
2325 _(b'forcibly copy over an existing managed file'),
2326 ),
2326 ),
2327 ]
2327 ]
2328 + walkopts
2328 + walkopts
2329 + dryrunopts,
2329 + dryrunopts,
2330 _(b'[OPTION]... SOURCE... DEST'),
2330 _(b'[OPTION]... SOURCE... DEST'),
2331 helpcategory=command.CATEGORY_FILE_CONTENTS,
2331 helpcategory=command.CATEGORY_FILE_CONTENTS,
2332 )
2332 )
2333 def copy(ui, repo, *pats, **opts):
2333 def copy(ui, repo, *pats, **opts):
2334 """mark files as copied for the next commit
2334 """mark files as copied for the next commit
2335
2335
2336 Mark dest as having copies of source files. If dest is a
2336 Mark dest as having copies of source files. If dest is a
2337 directory, copies are put in that directory. If dest is a file,
2337 directory, copies are put in that directory. If dest is a file,
2338 the source must be a single file.
2338 the source must be a single file.
2339
2339
2340 By default, this command copies the contents of files as they
2340 By default, this command copies the contents of files as they
2341 exist in the working directory. If invoked with -A/--after, the
2341 exist in the working directory. If invoked with -A/--after, the
2342 operation is recorded, but no copying is performed.
2342 operation is recorded, but no copying is performed.
2343
2343
2344 To undo marking a file as copied, use --forget. With that option,
2344 To undo marking a file as copied, use --forget. With that option,
2345 all given (positional) arguments are unmarked as copies. The destination
2345 all given (positional) arguments are unmarked as copies. The destination
2346 file(s) will be left in place (still tracked).
2346 file(s) will be left in place (still tracked).
2347
2347
2348 This command takes effect with the next commit by default.
2348 This command takes effect with the next commit by default.
2349
2349
2350 Returns 0 on success, 1 if errors are encountered.
2350 Returns 0 on success, 1 if errors are encountered.
2351 """
2351 """
2352 opts = pycompat.byteskwargs(opts)
2352 opts = pycompat.byteskwargs(opts)
2353 with repo.wlock():
2353 with repo.wlock():
2354 return cmdutil.copy(ui, repo, pats, opts)
2354 return cmdutil.copy(ui, repo, pats, opts)
2355
2355
2356
2356
2357 @command(
2357 @command(
2358 b'debugcommands',
2358 b'debugcommands',
2359 [],
2359 [],
2360 _(b'[COMMAND]'),
2360 _(b'[COMMAND]'),
2361 helpcategory=command.CATEGORY_HELP,
2361 helpcategory=command.CATEGORY_HELP,
2362 norepo=True,
2362 norepo=True,
2363 )
2363 )
2364 def debugcommands(ui, cmd=b'', *args):
2364 def debugcommands(ui, cmd=b'', *args):
2365 """list all available commands and options"""
2365 """list all available commands and options"""
2366 for cmd, vals in sorted(pycompat.iteritems(table)):
2366 for cmd, vals in sorted(pycompat.iteritems(table)):
2367 cmd = cmd.split(b'|')[0]
2367 cmd = cmd.split(b'|')[0]
2368 opts = b', '.join([i[1] for i in vals[1]])
2368 opts = b', '.join([i[1] for i in vals[1]])
2369 ui.write(b'%s: %s\n' % (cmd, opts))
2369 ui.write(b'%s: %s\n' % (cmd, opts))
2370
2370
2371
2371
2372 @command(
2372 @command(
2373 b'debugcomplete',
2373 b'debugcomplete',
2374 [(b'o', b'options', None, _(b'show the command options'))],
2374 [(b'o', b'options', None, _(b'show the command options'))],
2375 _(b'[-o] CMD'),
2375 _(b'[-o] CMD'),
2376 helpcategory=command.CATEGORY_HELP,
2376 helpcategory=command.CATEGORY_HELP,
2377 norepo=True,
2377 norepo=True,
2378 )
2378 )
2379 def debugcomplete(ui, cmd=b'', **opts):
2379 def debugcomplete(ui, cmd=b'', **opts):
2380 """returns the completion list associated with the given command"""
2380 """returns the completion list associated with the given command"""
2381
2381
2382 if opts.get('options'):
2382 if opts.get('options'):
2383 options = []
2383 options = []
2384 otables = [globalopts]
2384 otables = [globalopts]
2385 if cmd:
2385 if cmd:
2386 aliases, entry = cmdutil.findcmd(cmd, table, False)
2386 aliases, entry = cmdutil.findcmd(cmd, table, False)
2387 otables.append(entry[1])
2387 otables.append(entry[1])
2388 for t in otables:
2388 for t in otables:
2389 for o in t:
2389 for o in t:
2390 if b"(DEPRECATED)" in o[3]:
2390 if b"(DEPRECATED)" in o[3]:
2391 continue
2391 continue
2392 if o[0]:
2392 if o[0]:
2393 options.append(b'-%s' % o[0])
2393 options.append(b'-%s' % o[0])
2394 options.append(b'--%s' % o[1])
2394 options.append(b'--%s' % o[1])
2395 ui.write(b"%s\n" % b"\n".join(options))
2395 ui.write(b"%s\n" % b"\n".join(options))
2396 return
2396 return
2397
2397
2398 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2398 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2399 if ui.verbose:
2399 if ui.verbose:
2400 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2400 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2401 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2401 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2402
2402
2403
2403
2404 @command(
2404 @command(
2405 b'diff',
2405 b'diff',
2406 [
2406 [
2407 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2407 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2408 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2408 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2409 ]
2409 ]
2410 + diffopts
2410 + diffopts
2411 + diffopts2
2411 + diffopts2
2412 + walkopts
2412 + walkopts
2413 + subrepoopts,
2413 + subrepoopts,
2414 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2414 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2415 helpcategory=command.CATEGORY_FILE_CONTENTS,
2415 helpcategory=command.CATEGORY_FILE_CONTENTS,
2416 helpbasic=True,
2416 helpbasic=True,
2417 inferrepo=True,
2417 inferrepo=True,
2418 intents={INTENT_READONLY},
2418 intents={INTENT_READONLY},
2419 )
2419 )
2420 def diff(ui, repo, *pats, **opts):
2420 def diff(ui, repo, *pats, **opts):
2421 """diff repository (or selected files)
2421 """diff repository (or selected files)
2422
2422
2423 Show differences between revisions for the specified files.
2423 Show differences between revisions for the specified files.
2424
2424
2425 Differences between files are shown using the unified diff format.
2425 Differences between files are shown using the unified diff format.
2426
2426
2427 .. note::
2427 .. note::
2428
2428
2429 :hg:`diff` may generate unexpected results for merges, as it will
2429 :hg:`diff` may generate unexpected results for merges, as it will
2430 default to comparing against the working directory's first
2430 default to comparing against the working directory's first
2431 parent changeset if no revisions are specified.
2431 parent changeset if no revisions are specified.
2432
2432
2433 When two revision arguments are given, then changes are shown
2433 When two revision arguments are given, then changes are shown
2434 between those revisions. If only one revision is specified then
2434 between those revisions. If only one revision is specified then
2435 that revision is compared to the working directory, and, when no
2435 that revision is compared to the working directory, and, when no
2436 revisions are specified, the working directory files are compared
2436 revisions are specified, the working directory files are compared
2437 to its first parent.
2437 to its first parent.
2438
2438
2439 Alternatively you can specify -c/--change with a revision to see
2439 Alternatively you can specify -c/--change with a revision to see
2440 the changes in that changeset relative to its first parent.
2440 the changes in that changeset relative to its first parent.
2441
2441
2442 Without the -a/--text option, diff will avoid generating diffs of
2442 Without the -a/--text option, diff will avoid generating diffs of
2443 files it detects as binary. With -a, diff will generate a diff
2443 files it detects as binary. With -a, diff will generate a diff
2444 anyway, probably with undesirable results.
2444 anyway, probably with undesirable results.
2445
2445
2446 Use the -g/--git option to generate diffs in the git extended diff
2446 Use the -g/--git option to generate diffs in the git extended diff
2447 format. For more information, read :hg:`help diffs`.
2447 format. For more information, read :hg:`help diffs`.
2448
2448
2449 .. container:: verbose
2449 .. container:: verbose
2450
2450
2451 Examples:
2451 Examples:
2452
2452
2453 - compare a file in the current working directory to its parent::
2453 - compare a file in the current working directory to its parent::
2454
2454
2455 hg diff foo.c
2455 hg diff foo.c
2456
2456
2457 - compare two historical versions of a directory, with rename info::
2457 - compare two historical versions of a directory, with rename info::
2458
2458
2459 hg diff --git -r 1.0:1.2 lib/
2459 hg diff --git -r 1.0:1.2 lib/
2460
2460
2461 - get change stats relative to the last change on some date::
2461 - get change stats relative to the last change on some date::
2462
2462
2463 hg diff --stat -r "date('may 2')"
2463 hg diff --stat -r "date('may 2')"
2464
2464
2465 - diff all newly-added files that contain a keyword::
2465 - diff all newly-added files that contain a keyword::
2466
2466
2467 hg diff "set:added() and grep(GNU)"
2467 hg diff "set:added() and grep(GNU)"
2468
2468
2469 - compare a revision and its parents::
2469 - compare a revision and its parents::
2470
2470
2471 hg diff -c 9353 # compare against first parent
2471 hg diff -c 9353 # compare against first parent
2472 hg diff -r 9353^:9353 # same using revset syntax
2472 hg diff -r 9353^:9353 # same using revset syntax
2473 hg diff -r 9353^2:9353 # compare against the second parent
2473 hg diff -r 9353^2:9353 # compare against the second parent
2474
2474
2475 Returns 0 on success.
2475 Returns 0 on success.
2476 """
2476 """
2477
2477
2478 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2478 opts = pycompat.byteskwargs(opts)
2479 opts = pycompat.byteskwargs(opts)
2479 revs = opts.get(b'rev')
2480 revs = opts.get(b'rev')
2480 change = opts.get(b'change')
2481 change = opts.get(b'change')
2481 stat = opts.get(b'stat')
2482 stat = opts.get(b'stat')
2482 reverse = opts.get(b'reverse')
2483 reverse = opts.get(b'reverse')
2483
2484
2484 if revs and change:
2485 if change:
2485 msg = _(b'cannot specify --rev and --change at the same time')
2486 raise error.Abort(msg)
2487 elif change:
2488 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2486 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2489 ctx2 = scmutil.revsingle(repo, change, None)
2487 ctx2 = scmutil.revsingle(repo, change, None)
2490 ctx1 = ctx2.p1()
2488 ctx1 = ctx2.p1()
2491 else:
2489 else:
2492 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2490 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2493 ctx1, ctx2 = scmutil.revpair(repo, revs)
2491 ctx1, ctx2 = scmutil.revpair(repo, revs)
2494 node1, node2 = ctx1.node(), ctx2.node()
2492 node1, node2 = ctx1.node(), ctx2.node()
2495
2493
2496 if reverse:
2494 if reverse:
2497 node1, node2 = node2, node1
2495 node1, node2 = node2, node1
2498
2496
2499 diffopts = patch.diffallopts(ui, opts)
2497 diffopts = patch.diffallopts(ui, opts)
2500 m = scmutil.match(ctx2, pats, opts)
2498 m = scmutil.match(ctx2, pats, opts)
2501 m = repo.narrowmatch(m)
2499 m = repo.narrowmatch(m)
2502 ui.pager(b'diff')
2500 ui.pager(b'diff')
2503 logcmdutil.diffordiffstat(
2501 logcmdutil.diffordiffstat(
2504 ui,
2502 ui,
2505 repo,
2503 repo,
2506 diffopts,
2504 diffopts,
2507 node1,
2505 node1,
2508 node2,
2506 node2,
2509 m,
2507 m,
2510 stat=stat,
2508 stat=stat,
2511 listsubrepos=opts.get(b'subrepos'),
2509 listsubrepos=opts.get(b'subrepos'),
2512 root=opts.get(b'root'),
2510 root=opts.get(b'root'),
2513 )
2511 )
2514
2512
2515
2513
2516 @command(
2514 @command(
2517 b'export',
2515 b'export',
2518 [
2516 [
2519 (
2517 (
2520 b'B',
2518 b'B',
2521 b'bookmark',
2519 b'bookmark',
2522 b'',
2520 b'',
2523 _(b'export changes only reachable by given bookmark'),
2521 _(b'export changes only reachable by given bookmark'),
2524 _(b'BOOKMARK'),
2522 _(b'BOOKMARK'),
2525 ),
2523 ),
2526 (
2524 (
2527 b'o',
2525 b'o',
2528 b'output',
2526 b'output',
2529 b'',
2527 b'',
2530 _(b'print output to file with formatted name'),
2528 _(b'print output to file with formatted name'),
2531 _(b'FORMAT'),
2529 _(b'FORMAT'),
2532 ),
2530 ),
2533 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2531 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2534 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2532 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2535 ]
2533 ]
2536 + diffopts
2534 + diffopts
2537 + formatteropts,
2535 + formatteropts,
2538 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2536 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2539 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2537 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2540 helpbasic=True,
2538 helpbasic=True,
2541 intents={INTENT_READONLY},
2539 intents={INTENT_READONLY},
2542 )
2540 )
2543 def export(ui, repo, *changesets, **opts):
2541 def export(ui, repo, *changesets, **opts):
2544 """dump the header and diffs for one or more changesets
2542 """dump the header and diffs for one or more changesets
2545
2543
2546 Print the changeset header and diffs for one or more revisions.
2544 Print the changeset header and diffs for one or more revisions.
2547 If no revision is given, the parent of the working directory is used.
2545 If no revision is given, the parent of the working directory is used.
2548
2546
2549 The information shown in the changeset header is: author, date,
2547 The information shown in the changeset header is: author, date,
2550 branch name (if non-default), changeset hash, parent(s) and commit
2548 branch name (if non-default), changeset hash, parent(s) and commit
2551 comment.
2549 comment.
2552
2550
2553 .. note::
2551 .. note::
2554
2552
2555 :hg:`export` may generate unexpected diff output for merge
2553 :hg:`export` may generate unexpected diff output for merge
2556 changesets, as it will compare the merge changeset against its
2554 changesets, as it will compare the merge changeset against its
2557 first parent only.
2555 first parent only.
2558
2556
2559 Output may be to a file, in which case the name of the file is
2557 Output may be to a file, in which case the name of the file is
2560 given using a template string. See :hg:`help templates`. In addition
2558 given using a template string. See :hg:`help templates`. In addition
2561 to the common template keywords, the following formatting rules are
2559 to the common template keywords, the following formatting rules are
2562 supported:
2560 supported:
2563
2561
2564 :``%%``: literal "%" character
2562 :``%%``: literal "%" character
2565 :``%H``: changeset hash (40 hexadecimal digits)
2563 :``%H``: changeset hash (40 hexadecimal digits)
2566 :``%N``: number of patches being generated
2564 :``%N``: number of patches being generated
2567 :``%R``: changeset revision number
2565 :``%R``: changeset revision number
2568 :``%b``: basename of the exporting repository
2566 :``%b``: basename of the exporting repository
2569 :``%h``: short-form changeset hash (12 hexadecimal digits)
2567 :``%h``: short-form changeset hash (12 hexadecimal digits)
2570 :``%m``: first line of the commit message (only alphanumeric characters)
2568 :``%m``: first line of the commit message (only alphanumeric characters)
2571 :``%n``: zero-padded sequence number, starting at 1
2569 :``%n``: zero-padded sequence number, starting at 1
2572 :``%r``: zero-padded changeset revision number
2570 :``%r``: zero-padded changeset revision number
2573 :``\\``: literal "\\" character
2571 :``\\``: literal "\\" character
2574
2572
2575 Without the -a/--text option, export will avoid generating diffs
2573 Without the -a/--text option, export will avoid generating diffs
2576 of files it detects as binary. With -a, export will generate a
2574 of files it detects as binary. With -a, export will generate a
2577 diff anyway, probably with undesirable results.
2575 diff anyway, probably with undesirable results.
2578
2576
2579 With -B/--bookmark changesets reachable by the given bookmark are
2577 With -B/--bookmark changesets reachable by the given bookmark are
2580 selected.
2578 selected.
2581
2579
2582 Use the -g/--git option to generate diffs in the git extended diff
2580 Use the -g/--git option to generate diffs in the git extended diff
2583 format. See :hg:`help diffs` for more information.
2581 format. See :hg:`help diffs` for more information.
2584
2582
2585 With the --switch-parent option, the diff will be against the
2583 With the --switch-parent option, the diff will be against the
2586 second parent. It can be useful to review a merge.
2584 second parent. It can be useful to review a merge.
2587
2585
2588 .. container:: verbose
2586 .. container:: verbose
2589
2587
2590 Template:
2588 Template:
2591
2589
2592 The following keywords are supported in addition to the common template
2590 The following keywords are supported in addition to the common template
2593 keywords and functions. See also :hg:`help templates`.
2591 keywords and functions. See also :hg:`help templates`.
2594
2592
2595 :diff: String. Diff content.
2593 :diff: String. Diff content.
2596 :parents: List of strings. Parent nodes of the changeset.
2594 :parents: List of strings. Parent nodes of the changeset.
2597
2595
2598 Examples:
2596 Examples:
2599
2597
2600 - use export and import to transplant a bugfix to the current
2598 - use export and import to transplant a bugfix to the current
2601 branch::
2599 branch::
2602
2600
2603 hg export -r 9353 | hg import -
2601 hg export -r 9353 | hg import -
2604
2602
2605 - export all the changesets between two revisions to a file with
2603 - export all the changesets between two revisions to a file with
2606 rename information::
2604 rename information::
2607
2605
2608 hg export --git -r 123:150 > changes.txt
2606 hg export --git -r 123:150 > changes.txt
2609
2607
2610 - split outgoing changes into a series of patches with
2608 - split outgoing changes into a series of patches with
2611 descriptive names::
2609 descriptive names::
2612
2610
2613 hg export -r "outgoing()" -o "%n-%m.patch"
2611 hg export -r "outgoing()" -o "%n-%m.patch"
2614
2612
2615 Returns 0 on success.
2613 Returns 0 on success.
2616 """
2614 """
2617 opts = pycompat.byteskwargs(opts)
2615 opts = pycompat.byteskwargs(opts)
2618 bookmark = opts.get(b'bookmark')
2616 bookmark = opts.get(b'bookmark')
2619 changesets += tuple(opts.get(b'rev', []))
2617 changesets += tuple(opts.get(b'rev', []))
2620
2618
2621 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2619 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2622
2620
2623 if bookmark:
2621 if bookmark:
2624 if bookmark not in repo._bookmarks:
2622 if bookmark not in repo._bookmarks:
2625 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2623 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2626
2624
2627 revs = scmutil.bookmarkrevs(repo, bookmark)
2625 revs = scmutil.bookmarkrevs(repo, bookmark)
2628 else:
2626 else:
2629 if not changesets:
2627 if not changesets:
2630 changesets = [b'.']
2628 changesets = [b'.']
2631
2629
2632 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2630 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2633 revs = scmutil.revrange(repo, changesets)
2631 revs = scmutil.revrange(repo, changesets)
2634
2632
2635 if not revs:
2633 if not revs:
2636 raise error.Abort(_(b"export requires at least one changeset"))
2634 raise error.Abort(_(b"export requires at least one changeset"))
2637 if len(revs) > 1:
2635 if len(revs) > 1:
2638 ui.note(_(b'exporting patches:\n'))
2636 ui.note(_(b'exporting patches:\n'))
2639 else:
2637 else:
2640 ui.note(_(b'exporting patch:\n'))
2638 ui.note(_(b'exporting patch:\n'))
2641
2639
2642 fntemplate = opts.get(b'output')
2640 fntemplate = opts.get(b'output')
2643 if cmdutil.isstdiofilename(fntemplate):
2641 if cmdutil.isstdiofilename(fntemplate):
2644 fntemplate = b''
2642 fntemplate = b''
2645
2643
2646 if fntemplate:
2644 if fntemplate:
2647 fm = formatter.nullformatter(ui, b'export', opts)
2645 fm = formatter.nullformatter(ui, b'export', opts)
2648 else:
2646 else:
2649 ui.pager(b'export')
2647 ui.pager(b'export')
2650 fm = ui.formatter(b'export', opts)
2648 fm = ui.formatter(b'export', opts)
2651 with fm:
2649 with fm:
2652 cmdutil.export(
2650 cmdutil.export(
2653 repo,
2651 repo,
2654 revs,
2652 revs,
2655 fm,
2653 fm,
2656 fntemplate=fntemplate,
2654 fntemplate=fntemplate,
2657 switch_parent=opts.get(b'switch_parent'),
2655 switch_parent=opts.get(b'switch_parent'),
2658 opts=patch.diffallopts(ui, opts),
2656 opts=patch.diffallopts(ui, opts),
2659 )
2657 )
2660
2658
2661
2659
2662 @command(
2660 @command(
2663 b'files',
2661 b'files',
2664 [
2662 [
2665 (
2663 (
2666 b'r',
2664 b'r',
2667 b'rev',
2665 b'rev',
2668 b'',
2666 b'',
2669 _(b'search the repository as it is in REV'),
2667 _(b'search the repository as it is in REV'),
2670 _(b'REV'),
2668 _(b'REV'),
2671 ),
2669 ),
2672 (
2670 (
2673 b'0',
2671 b'0',
2674 b'print0',
2672 b'print0',
2675 None,
2673 None,
2676 _(b'end filenames with NUL, for use with xargs'),
2674 _(b'end filenames with NUL, for use with xargs'),
2677 ),
2675 ),
2678 ]
2676 ]
2679 + walkopts
2677 + walkopts
2680 + formatteropts
2678 + formatteropts
2681 + subrepoopts,
2679 + subrepoopts,
2682 _(b'[OPTION]... [FILE]...'),
2680 _(b'[OPTION]... [FILE]...'),
2683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2681 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2684 intents={INTENT_READONLY},
2682 intents={INTENT_READONLY},
2685 )
2683 )
2686 def files(ui, repo, *pats, **opts):
2684 def files(ui, repo, *pats, **opts):
2687 """list tracked files
2685 """list tracked files
2688
2686
2689 Print files under Mercurial control in the working directory or
2687 Print files under Mercurial control in the working directory or
2690 specified revision for given files (excluding removed files).
2688 specified revision for given files (excluding removed files).
2691 Files can be specified as filenames or filesets.
2689 Files can be specified as filenames or filesets.
2692
2690
2693 If no files are given to match, this command prints the names
2691 If no files are given to match, this command prints the names
2694 of all files under Mercurial control.
2692 of all files under Mercurial control.
2695
2693
2696 .. container:: verbose
2694 .. container:: verbose
2697
2695
2698 Template:
2696 Template:
2699
2697
2700 The following keywords are supported in addition to the common template
2698 The following keywords are supported in addition to the common template
2701 keywords and functions. See also :hg:`help templates`.
2699 keywords and functions. See also :hg:`help templates`.
2702
2700
2703 :flags: String. Character denoting file's symlink and executable bits.
2701 :flags: String. Character denoting file's symlink and executable bits.
2704 :path: String. Repository-absolute path of the file.
2702 :path: String. Repository-absolute path of the file.
2705 :size: Integer. Size of the file in bytes.
2703 :size: Integer. Size of the file in bytes.
2706
2704
2707 Examples:
2705 Examples:
2708
2706
2709 - list all files under the current directory::
2707 - list all files under the current directory::
2710
2708
2711 hg files .
2709 hg files .
2712
2710
2713 - shows sizes and flags for current revision::
2711 - shows sizes and flags for current revision::
2714
2712
2715 hg files -vr .
2713 hg files -vr .
2716
2714
2717 - list all files named README::
2715 - list all files named README::
2718
2716
2719 hg files -I "**/README"
2717 hg files -I "**/README"
2720
2718
2721 - list all binary files::
2719 - list all binary files::
2722
2720
2723 hg files "set:binary()"
2721 hg files "set:binary()"
2724
2722
2725 - find files containing a regular expression::
2723 - find files containing a regular expression::
2726
2724
2727 hg files "set:grep('bob')"
2725 hg files "set:grep('bob')"
2728
2726
2729 - search tracked file contents with xargs and grep::
2727 - search tracked file contents with xargs and grep::
2730
2728
2731 hg files -0 | xargs -0 grep foo
2729 hg files -0 | xargs -0 grep foo
2732
2730
2733 See :hg:`help patterns` and :hg:`help filesets` for more information
2731 See :hg:`help patterns` and :hg:`help filesets` for more information
2734 on specifying file patterns.
2732 on specifying file patterns.
2735
2733
2736 Returns 0 if a match is found, 1 otherwise.
2734 Returns 0 if a match is found, 1 otherwise.
2737
2735
2738 """
2736 """
2739
2737
2740 opts = pycompat.byteskwargs(opts)
2738 opts = pycompat.byteskwargs(opts)
2741 rev = opts.get(b'rev')
2739 rev = opts.get(b'rev')
2742 if rev:
2740 if rev:
2743 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2741 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2744 ctx = scmutil.revsingle(repo, rev, None)
2742 ctx = scmutil.revsingle(repo, rev, None)
2745
2743
2746 end = b'\n'
2744 end = b'\n'
2747 if opts.get(b'print0'):
2745 if opts.get(b'print0'):
2748 end = b'\0'
2746 end = b'\0'
2749 fmt = b'%s' + end
2747 fmt = b'%s' + end
2750
2748
2751 m = scmutil.match(ctx, pats, opts)
2749 m = scmutil.match(ctx, pats, opts)
2752 ui.pager(b'files')
2750 ui.pager(b'files')
2753 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2751 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2754 with ui.formatter(b'files', opts) as fm:
2752 with ui.formatter(b'files', opts) as fm:
2755 return cmdutil.files(
2753 return cmdutil.files(
2756 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2754 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2757 )
2755 )
2758
2756
2759
2757
2760 @command(
2758 @command(
2761 b'forget',
2759 b'forget',
2762 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2760 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2763 + walkopts
2761 + walkopts
2764 + dryrunopts,
2762 + dryrunopts,
2765 _(b'[OPTION]... FILE...'),
2763 _(b'[OPTION]... FILE...'),
2766 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2764 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2767 helpbasic=True,
2765 helpbasic=True,
2768 inferrepo=True,
2766 inferrepo=True,
2769 )
2767 )
2770 def forget(ui, repo, *pats, **opts):
2768 def forget(ui, repo, *pats, **opts):
2771 """forget the specified files on the next commit
2769 """forget the specified files on the next commit
2772
2770
2773 Mark the specified files so they will no longer be tracked
2771 Mark the specified files so they will no longer be tracked
2774 after the next commit.
2772 after the next commit.
2775
2773
2776 This only removes files from the current branch, not from the
2774 This only removes files from the current branch, not from the
2777 entire project history, and it does not delete them from the
2775 entire project history, and it does not delete them from the
2778 working directory.
2776 working directory.
2779
2777
2780 To delete the file from the working directory, see :hg:`remove`.
2778 To delete the file from the working directory, see :hg:`remove`.
2781
2779
2782 To undo a forget before the next commit, see :hg:`add`.
2780 To undo a forget before the next commit, see :hg:`add`.
2783
2781
2784 .. container:: verbose
2782 .. container:: verbose
2785
2783
2786 Examples:
2784 Examples:
2787
2785
2788 - forget newly-added binary files::
2786 - forget newly-added binary files::
2789
2787
2790 hg forget "set:added() and binary()"
2788 hg forget "set:added() and binary()"
2791
2789
2792 - forget files that would be excluded by .hgignore::
2790 - forget files that would be excluded by .hgignore::
2793
2791
2794 hg forget "set:hgignore()"
2792 hg forget "set:hgignore()"
2795
2793
2796 Returns 0 on success.
2794 Returns 0 on success.
2797 """
2795 """
2798
2796
2799 opts = pycompat.byteskwargs(opts)
2797 opts = pycompat.byteskwargs(opts)
2800 if not pats:
2798 if not pats:
2801 raise error.Abort(_(b'no files specified'))
2799 raise error.Abort(_(b'no files specified'))
2802
2800
2803 m = scmutil.match(repo[None], pats, opts)
2801 m = scmutil.match(repo[None], pats, opts)
2804 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2802 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2805 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2803 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2806 rejected = cmdutil.forget(
2804 rejected = cmdutil.forget(
2807 ui,
2805 ui,
2808 repo,
2806 repo,
2809 m,
2807 m,
2810 prefix=b"",
2808 prefix=b"",
2811 uipathfn=uipathfn,
2809 uipathfn=uipathfn,
2812 explicitonly=False,
2810 explicitonly=False,
2813 dryrun=dryrun,
2811 dryrun=dryrun,
2814 interactive=interactive,
2812 interactive=interactive,
2815 )[0]
2813 )[0]
2816 return rejected and 1 or 0
2814 return rejected and 1 or 0
2817
2815
2818
2816
2819 @command(
2817 @command(
2820 b'graft',
2818 b'graft',
2821 [
2819 [
2822 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2820 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2823 (
2821 (
2824 b'',
2822 b'',
2825 b'base',
2823 b'base',
2826 b'',
2824 b'',
2827 _(b'base revision when doing the graft merge (ADVANCED)'),
2825 _(b'base revision when doing the graft merge (ADVANCED)'),
2828 _(b'REV'),
2826 _(b'REV'),
2829 ),
2827 ),
2830 (b'c', b'continue', False, _(b'resume interrupted graft')),
2828 (b'c', b'continue', False, _(b'resume interrupted graft')),
2831 (b'', b'stop', False, _(b'stop interrupted graft')),
2829 (b'', b'stop', False, _(b'stop interrupted graft')),
2832 (b'', b'abort', False, _(b'abort interrupted graft')),
2830 (b'', b'abort', False, _(b'abort interrupted graft')),
2833 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2831 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2834 (b'', b'log', None, _(b'append graft info to log message')),
2832 (b'', b'log', None, _(b'append graft info to log message')),
2835 (
2833 (
2836 b'',
2834 b'',
2837 b'no-commit',
2835 b'no-commit',
2838 None,
2836 None,
2839 _(b"don't commit, just apply the changes in working directory"),
2837 _(b"don't commit, just apply the changes in working directory"),
2840 ),
2838 ),
2841 (b'f', b'force', False, _(b'force graft')),
2839 (b'f', b'force', False, _(b'force graft')),
2842 (
2840 (
2843 b'D',
2841 b'D',
2844 b'currentdate',
2842 b'currentdate',
2845 False,
2843 False,
2846 _(b'record the current date as commit date'),
2844 _(b'record the current date as commit date'),
2847 ),
2845 ),
2848 (
2846 (
2849 b'U',
2847 b'U',
2850 b'currentuser',
2848 b'currentuser',
2851 False,
2849 False,
2852 _(b'record the current user as committer'),
2850 _(b'record the current user as committer'),
2853 ),
2851 ),
2854 ]
2852 ]
2855 + commitopts2
2853 + commitopts2
2856 + mergetoolopts
2854 + mergetoolopts
2857 + dryrunopts,
2855 + dryrunopts,
2858 _(b'[OPTION]... [-r REV]... REV...'),
2856 _(b'[OPTION]... [-r REV]... REV...'),
2859 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2857 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2860 )
2858 )
2861 def graft(ui, repo, *revs, **opts):
2859 def graft(ui, repo, *revs, **opts):
2862 '''copy changes from other branches onto the current branch
2860 '''copy changes from other branches onto the current branch
2863
2861
2864 This command uses Mercurial's merge logic to copy individual
2862 This command uses Mercurial's merge logic to copy individual
2865 changes from other branches without merging branches in the
2863 changes from other branches without merging branches in the
2866 history graph. This is sometimes known as 'backporting' or
2864 history graph. This is sometimes known as 'backporting' or
2867 'cherry-picking'. By default, graft will copy user, date, and
2865 'cherry-picking'. By default, graft will copy user, date, and
2868 description from the source changesets.
2866 description from the source changesets.
2869
2867
2870 Changesets that are ancestors of the current revision, that have
2868 Changesets that are ancestors of the current revision, that have
2871 already been grafted, or that are merges will be skipped.
2869 already been grafted, or that are merges will be skipped.
2872
2870
2873 If --log is specified, log messages will have a comment appended
2871 If --log is specified, log messages will have a comment appended
2874 of the form::
2872 of the form::
2875
2873
2876 (grafted from CHANGESETHASH)
2874 (grafted from CHANGESETHASH)
2877
2875
2878 If --force is specified, revisions will be grafted even if they
2876 If --force is specified, revisions will be grafted even if they
2879 are already ancestors of, or have been grafted to, the destination.
2877 are already ancestors of, or have been grafted to, the destination.
2880 This is useful when the revisions have since been backed out.
2878 This is useful when the revisions have since been backed out.
2881
2879
2882 If a graft merge results in conflicts, the graft process is
2880 If a graft merge results in conflicts, the graft process is
2883 interrupted so that the current merge can be manually resolved.
2881 interrupted so that the current merge can be manually resolved.
2884 Once all conflicts are addressed, the graft process can be
2882 Once all conflicts are addressed, the graft process can be
2885 continued with the -c/--continue option.
2883 continued with the -c/--continue option.
2886
2884
2887 The -c/--continue option reapplies all the earlier options.
2885 The -c/--continue option reapplies all the earlier options.
2888
2886
2889 .. container:: verbose
2887 .. container:: verbose
2890
2888
2891 The --base option exposes more of how graft internally uses merge with a
2889 The --base option exposes more of how graft internally uses merge with a
2892 custom base revision. --base can be used to specify another ancestor than
2890 custom base revision. --base can be used to specify another ancestor than
2893 the first and only parent.
2891 the first and only parent.
2894
2892
2895 The command::
2893 The command::
2896
2894
2897 hg graft -r 345 --base 234
2895 hg graft -r 345 --base 234
2898
2896
2899 is thus pretty much the same as::
2897 is thus pretty much the same as::
2900
2898
2901 hg diff -r 234 -r 345 | hg import
2899 hg diff -r 234 -r 345 | hg import
2902
2900
2903 but using merge to resolve conflicts and track moved files.
2901 but using merge to resolve conflicts and track moved files.
2904
2902
2905 The result of a merge can thus be backported as a single commit by
2903 The result of a merge can thus be backported as a single commit by
2906 specifying one of the merge parents as base, and thus effectively
2904 specifying one of the merge parents as base, and thus effectively
2907 grafting the changes from the other side.
2905 grafting the changes from the other side.
2908
2906
2909 It is also possible to collapse multiple changesets and clean up history
2907 It is also possible to collapse multiple changesets and clean up history
2910 by specifying another ancestor as base, much like rebase --collapse
2908 by specifying another ancestor as base, much like rebase --collapse
2911 --keep.
2909 --keep.
2912
2910
2913 The commit message can be tweaked after the fact using commit --amend .
2911 The commit message can be tweaked after the fact using commit --amend .
2914
2912
2915 For using non-ancestors as the base to backout changes, see the backout
2913 For using non-ancestors as the base to backout changes, see the backout
2916 command and the hidden --parent option.
2914 command and the hidden --parent option.
2917
2915
2918 .. container:: verbose
2916 .. container:: verbose
2919
2917
2920 Examples:
2918 Examples:
2921
2919
2922 - copy a single change to the stable branch and edit its description::
2920 - copy a single change to the stable branch and edit its description::
2923
2921
2924 hg update stable
2922 hg update stable
2925 hg graft --edit 9393
2923 hg graft --edit 9393
2926
2924
2927 - graft a range of changesets with one exception, updating dates::
2925 - graft a range of changesets with one exception, updating dates::
2928
2926
2929 hg graft -D "2085::2093 and not 2091"
2927 hg graft -D "2085::2093 and not 2091"
2930
2928
2931 - continue a graft after resolving conflicts::
2929 - continue a graft after resolving conflicts::
2932
2930
2933 hg graft -c
2931 hg graft -c
2934
2932
2935 - show the source of a grafted changeset::
2933 - show the source of a grafted changeset::
2936
2934
2937 hg log --debug -r .
2935 hg log --debug -r .
2938
2936
2939 - show revisions sorted by date::
2937 - show revisions sorted by date::
2940
2938
2941 hg log -r "sort(all(), date)"
2939 hg log -r "sort(all(), date)"
2942
2940
2943 - backport the result of a merge as a single commit::
2941 - backport the result of a merge as a single commit::
2944
2942
2945 hg graft -r 123 --base 123^
2943 hg graft -r 123 --base 123^
2946
2944
2947 - land a feature branch as one changeset::
2945 - land a feature branch as one changeset::
2948
2946
2949 hg up -cr default
2947 hg up -cr default
2950 hg graft -r featureX --base "ancestor('featureX', 'default')"
2948 hg graft -r featureX --base "ancestor('featureX', 'default')"
2951
2949
2952 See :hg:`help revisions` for more about specifying revisions.
2950 See :hg:`help revisions` for more about specifying revisions.
2953
2951
2954 Returns 0 on successful completion, 1 if there are unresolved files.
2952 Returns 0 on successful completion, 1 if there are unresolved files.
2955 '''
2953 '''
2956 with repo.wlock():
2954 with repo.wlock():
2957 return _dograft(ui, repo, *revs, **opts)
2955 return _dograft(ui, repo, *revs, **opts)
2958
2956
2959
2957
2960 def _dograft(ui, repo, *revs, **opts):
2958 def _dograft(ui, repo, *revs, **opts):
2961 opts = pycompat.byteskwargs(opts)
2959 opts = pycompat.byteskwargs(opts)
2962 if revs and opts.get(b'rev'):
2960 if revs and opts.get(b'rev'):
2963 ui.warn(
2961 ui.warn(
2964 _(
2962 _(
2965 b'warning: inconsistent use of --rev might give unexpected '
2963 b'warning: inconsistent use of --rev might give unexpected '
2966 b'revision ordering!\n'
2964 b'revision ordering!\n'
2967 )
2965 )
2968 )
2966 )
2969
2967
2970 revs = list(revs)
2968 revs = list(revs)
2971 revs.extend(opts.get(b'rev'))
2969 revs.extend(opts.get(b'rev'))
2972 basectx = None
2970 basectx = None
2973 if opts.get(b'base'):
2971 if opts.get(b'base'):
2974 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2972 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2975 # a dict of data to be stored in state file
2973 # a dict of data to be stored in state file
2976 statedata = {}
2974 statedata = {}
2977 # list of new nodes created by ongoing graft
2975 # list of new nodes created by ongoing graft
2978 statedata[b'newnodes'] = []
2976 statedata[b'newnodes'] = []
2979
2977
2980 cmdutil.resolvecommitoptions(ui, opts)
2978 cmdutil.resolvecommitoptions(ui, opts)
2981
2979
2982 editor = cmdutil.getcommiteditor(
2980 editor = cmdutil.getcommiteditor(
2983 editform=b'graft', **pycompat.strkwargs(opts)
2981 editform=b'graft', **pycompat.strkwargs(opts)
2984 )
2982 )
2985
2983
2986 cont = False
2984 cont = False
2987 if opts.get(b'no_commit'):
2985 if opts.get(b'no_commit'):
2988 if opts.get(b'edit'):
2986 if opts.get(b'edit'):
2989 raise error.Abort(
2987 raise error.Abort(
2990 _(b"cannot specify --no-commit and --edit together")
2988 _(b"cannot specify --no-commit and --edit together")
2991 )
2989 )
2992 if opts.get(b'currentuser'):
2990 if opts.get(b'currentuser'):
2993 raise error.Abort(
2991 raise error.Abort(
2994 _(b"cannot specify --no-commit and --currentuser together")
2992 _(b"cannot specify --no-commit and --currentuser together")
2995 )
2993 )
2996 if opts.get(b'currentdate'):
2994 if opts.get(b'currentdate'):
2997 raise error.Abort(
2995 raise error.Abort(
2998 _(b"cannot specify --no-commit and --currentdate together")
2996 _(b"cannot specify --no-commit and --currentdate together")
2999 )
2997 )
3000 if opts.get(b'log'):
2998 if opts.get(b'log'):
3001 raise error.Abort(
2999 raise error.Abort(
3002 _(b"cannot specify --no-commit and --log together")
3000 _(b"cannot specify --no-commit and --log together")
3003 )
3001 )
3004
3002
3005 graftstate = statemod.cmdstate(repo, b'graftstate')
3003 graftstate = statemod.cmdstate(repo, b'graftstate')
3006
3004
3007 if opts.get(b'stop'):
3005 if opts.get(b'stop'):
3008 if opts.get(b'continue'):
3006 if opts.get(b'continue'):
3009 raise error.Abort(
3007 raise error.Abort(
3010 _(b"cannot use '--continue' and '--stop' together")
3008 _(b"cannot use '--continue' and '--stop' together")
3011 )
3009 )
3012 if opts.get(b'abort'):
3010 if opts.get(b'abort'):
3013 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3011 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3014
3012
3015 if any(
3013 if any(
3016 (
3014 (
3017 opts.get(b'edit'),
3015 opts.get(b'edit'),
3018 opts.get(b'log'),
3016 opts.get(b'log'),
3019 opts.get(b'user'),
3017 opts.get(b'user'),
3020 opts.get(b'date'),
3018 opts.get(b'date'),
3021 opts.get(b'currentdate'),
3019 opts.get(b'currentdate'),
3022 opts.get(b'currentuser'),
3020 opts.get(b'currentuser'),
3023 opts.get(b'rev'),
3021 opts.get(b'rev'),
3024 )
3022 )
3025 ):
3023 ):
3026 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3024 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3027 return _stopgraft(ui, repo, graftstate)
3025 return _stopgraft(ui, repo, graftstate)
3028 elif opts.get(b'abort'):
3026 elif opts.get(b'abort'):
3029 if opts.get(b'continue'):
3027 if opts.get(b'continue'):
3030 raise error.Abort(
3028 raise error.Abort(
3031 _(b"cannot use '--continue' and '--abort' together")
3029 _(b"cannot use '--continue' and '--abort' together")
3032 )
3030 )
3033 if any(
3031 if any(
3034 (
3032 (
3035 opts.get(b'edit'),
3033 opts.get(b'edit'),
3036 opts.get(b'log'),
3034 opts.get(b'log'),
3037 opts.get(b'user'),
3035 opts.get(b'user'),
3038 opts.get(b'date'),
3036 opts.get(b'date'),
3039 opts.get(b'currentdate'),
3037 opts.get(b'currentdate'),
3040 opts.get(b'currentuser'),
3038 opts.get(b'currentuser'),
3041 opts.get(b'rev'),
3039 opts.get(b'rev'),
3042 )
3040 )
3043 ):
3041 ):
3044 raise error.Abort(
3042 raise error.Abort(
3045 _(b"cannot specify any other flag with '--abort'")
3043 _(b"cannot specify any other flag with '--abort'")
3046 )
3044 )
3047
3045
3048 return cmdutil.abortgraft(ui, repo, graftstate)
3046 return cmdutil.abortgraft(ui, repo, graftstate)
3049 elif opts.get(b'continue'):
3047 elif opts.get(b'continue'):
3050 cont = True
3048 cont = True
3051 if revs:
3049 if revs:
3052 raise error.Abort(_(b"can't specify --continue and revisions"))
3050 raise error.Abort(_(b"can't specify --continue and revisions"))
3053 # read in unfinished revisions
3051 # read in unfinished revisions
3054 if graftstate.exists():
3052 if graftstate.exists():
3055 statedata = cmdutil.readgraftstate(repo, graftstate)
3053 statedata = cmdutil.readgraftstate(repo, graftstate)
3056 if statedata.get(b'date'):
3054 if statedata.get(b'date'):
3057 opts[b'date'] = statedata[b'date']
3055 opts[b'date'] = statedata[b'date']
3058 if statedata.get(b'user'):
3056 if statedata.get(b'user'):
3059 opts[b'user'] = statedata[b'user']
3057 opts[b'user'] = statedata[b'user']
3060 if statedata.get(b'log'):
3058 if statedata.get(b'log'):
3061 opts[b'log'] = True
3059 opts[b'log'] = True
3062 if statedata.get(b'no_commit'):
3060 if statedata.get(b'no_commit'):
3063 opts[b'no_commit'] = statedata.get(b'no_commit')
3061 opts[b'no_commit'] = statedata.get(b'no_commit')
3064 nodes = statedata[b'nodes']
3062 nodes = statedata[b'nodes']
3065 revs = [repo[node].rev() for node in nodes]
3063 revs = [repo[node].rev() for node in nodes]
3066 else:
3064 else:
3067 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3065 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3068 else:
3066 else:
3069 if not revs:
3067 if not revs:
3070 raise error.Abort(_(b'no revisions specified'))
3068 raise error.Abort(_(b'no revisions specified'))
3071 cmdutil.checkunfinished(repo)
3069 cmdutil.checkunfinished(repo)
3072 cmdutil.bailifchanged(repo)
3070 cmdutil.bailifchanged(repo)
3073 revs = scmutil.revrange(repo, revs)
3071 revs = scmutil.revrange(repo, revs)
3074
3072
3075 skipped = set()
3073 skipped = set()
3076 if basectx is None:
3074 if basectx is None:
3077 # check for merges
3075 # check for merges
3078 for rev in repo.revs(b'%ld and merge()', revs):
3076 for rev in repo.revs(b'%ld and merge()', revs):
3079 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3077 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3080 skipped.add(rev)
3078 skipped.add(rev)
3081 revs = [r for r in revs if r not in skipped]
3079 revs = [r for r in revs if r not in skipped]
3082 if not revs:
3080 if not revs:
3083 return -1
3081 return -1
3084 if basectx is not None and len(revs) != 1:
3082 if basectx is not None and len(revs) != 1:
3085 raise error.Abort(_(b'only one revision allowed with --base '))
3083 raise error.Abort(_(b'only one revision allowed with --base '))
3086
3084
3087 # Don't check in the --continue case, in effect retaining --force across
3085 # Don't check in the --continue case, in effect retaining --force across
3088 # --continues. That's because without --force, any revisions we decided to
3086 # --continues. That's because without --force, any revisions we decided to
3089 # skip would have been filtered out here, so they wouldn't have made their
3087 # skip would have been filtered out here, so they wouldn't have made their
3090 # way to the graftstate. With --force, any revisions we would have otherwise
3088 # way to the graftstate. With --force, any revisions we would have otherwise
3091 # skipped would not have been filtered out, and if they hadn't been applied
3089 # skipped would not have been filtered out, and if they hadn't been applied
3092 # already, they'd have been in the graftstate.
3090 # already, they'd have been in the graftstate.
3093 if not (cont or opts.get(b'force')) and basectx is None:
3091 if not (cont or opts.get(b'force')) and basectx is None:
3094 # check for ancestors of dest branch
3092 # check for ancestors of dest branch
3095 ancestors = repo.revs(b'%ld & (::.)', revs)
3093 ancestors = repo.revs(b'%ld & (::.)', revs)
3096 for rev in ancestors:
3094 for rev in ancestors:
3097 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3095 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3098
3096
3099 revs = [r for r in revs if r not in ancestors]
3097 revs = [r for r in revs if r not in ancestors]
3100
3098
3101 if not revs:
3099 if not revs:
3102 return -1
3100 return -1
3103
3101
3104 # analyze revs for earlier grafts
3102 # analyze revs for earlier grafts
3105 ids = {}
3103 ids = {}
3106 for ctx in repo.set(b"%ld", revs):
3104 for ctx in repo.set(b"%ld", revs):
3107 ids[ctx.hex()] = ctx.rev()
3105 ids[ctx.hex()] = ctx.rev()
3108 n = ctx.extra().get(b'source')
3106 n = ctx.extra().get(b'source')
3109 if n:
3107 if n:
3110 ids[n] = ctx.rev()
3108 ids[n] = ctx.rev()
3111
3109
3112 # check ancestors for earlier grafts
3110 # check ancestors for earlier grafts
3113 ui.debug(b'scanning for duplicate grafts\n')
3111 ui.debug(b'scanning for duplicate grafts\n')
3114
3112
3115 # The only changesets we can be sure doesn't contain grafts of any
3113 # The only changesets we can be sure doesn't contain grafts of any
3116 # revs, are the ones that are common ancestors of *all* revs:
3114 # revs, are the ones that are common ancestors of *all* revs:
3117 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3115 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3118 ctx = repo[rev]
3116 ctx = repo[rev]
3119 n = ctx.extra().get(b'source')
3117 n = ctx.extra().get(b'source')
3120 if n in ids:
3118 if n in ids:
3121 try:
3119 try:
3122 r = repo[n].rev()
3120 r = repo[n].rev()
3123 except error.RepoLookupError:
3121 except error.RepoLookupError:
3124 r = None
3122 r = None
3125 if r in revs:
3123 if r in revs:
3126 ui.warn(
3124 ui.warn(
3127 _(
3125 _(
3128 b'skipping revision %d:%s '
3126 b'skipping revision %d:%s '
3129 b'(already grafted to %d:%s)\n'
3127 b'(already grafted to %d:%s)\n'
3130 )
3128 )
3131 % (r, repo[r], rev, ctx)
3129 % (r, repo[r], rev, ctx)
3132 )
3130 )
3133 revs.remove(r)
3131 revs.remove(r)
3134 elif ids[n] in revs:
3132 elif ids[n] in revs:
3135 if r is None:
3133 if r is None:
3136 ui.warn(
3134 ui.warn(
3137 _(
3135 _(
3138 b'skipping already grafted revision %d:%s '
3136 b'skipping already grafted revision %d:%s '
3139 b'(%d:%s also has unknown origin %s)\n'
3137 b'(%d:%s also has unknown origin %s)\n'
3140 )
3138 )
3141 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3139 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3142 )
3140 )
3143 else:
3141 else:
3144 ui.warn(
3142 ui.warn(
3145 _(
3143 _(
3146 b'skipping already grafted revision %d:%s '
3144 b'skipping already grafted revision %d:%s '
3147 b'(%d:%s also has origin %d:%s)\n'
3145 b'(%d:%s also has origin %d:%s)\n'
3148 )
3146 )
3149 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3147 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3150 )
3148 )
3151 revs.remove(ids[n])
3149 revs.remove(ids[n])
3152 elif ctx.hex() in ids:
3150 elif ctx.hex() in ids:
3153 r = ids[ctx.hex()]
3151 r = ids[ctx.hex()]
3154 if r in revs:
3152 if r in revs:
3155 ui.warn(
3153 ui.warn(
3156 _(
3154 _(
3157 b'skipping already grafted revision %d:%s '
3155 b'skipping already grafted revision %d:%s '
3158 b'(was grafted from %d:%s)\n'
3156 b'(was grafted from %d:%s)\n'
3159 )
3157 )
3160 % (r, repo[r], rev, ctx)
3158 % (r, repo[r], rev, ctx)
3161 )
3159 )
3162 revs.remove(r)
3160 revs.remove(r)
3163 if not revs:
3161 if not revs:
3164 return -1
3162 return -1
3165
3163
3166 if opts.get(b'no_commit'):
3164 if opts.get(b'no_commit'):
3167 statedata[b'no_commit'] = True
3165 statedata[b'no_commit'] = True
3168 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3166 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3169 desc = b'%d:%s "%s"' % (
3167 desc = b'%d:%s "%s"' % (
3170 ctx.rev(),
3168 ctx.rev(),
3171 ctx,
3169 ctx,
3172 ctx.description().split(b'\n', 1)[0],
3170 ctx.description().split(b'\n', 1)[0],
3173 )
3171 )
3174 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3172 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3175 if names:
3173 if names:
3176 desc += b' (%s)' % b' '.join(names)
3174 desc += b' (%s)' % b' '.join(names)
3177 ui.status(_(b'grafting %s\n') % desc)
3175 ui.status(_(b'grafting %s\n') % desc)
3178 if opts.get(b'dry_run'):
3176 if opts.get(b'dry_run'):
3179 continue
3177 continue
3180
3178
3181 source = ctx.extra().get(b'source')
3179 source = ctx.extra().get(b'source')
3182 extra = {}
3180 extra = {}
3183 if source:
3181 if source:
3184 extra[b'source'] = source
3182 extra[b'source'] = source
3185 extra[b'intermediate-source'] = ctx.hex()
3183 extra[b'intermediate-source'] = ctx.hex()
3186 else:
3184 else:
3187 extra[b'source'] = ctx.hex()
3185 extra[b'source'] = ctx.hex()
3188 user = ctx.user()
3186 user = ctx.user()
3189 if opts.get(b'user'):
3187 if opts.get(b'user'):
3190 user = opts[b'user']
3188 user = opts[b'user']
3191 statedata[b'user'] = user
3189 statedata[b'user'] = user
3192 date = ctx.date()
3190 date = ctx.date()
3193 if opts.get(b'date'):
3191 if opts.get(b'date'):
3194 date = opts[b'date']
3192 date = opts[b'date']
3195 statedata[b'date'] = date
3193 statedata[b'date'] = date
3196 message = ctx.description()
3194 message = ctx.description()
3197 if opts.get(b'log'):
3195 if opts.get(b'log'):
3198 message += b'\n(grafted from %s)' % ctx.hex()
3196 message += b'\n(grafted from %s)' % ctx.hex()
3199 statedata[b'log'] = True
3197 statedata[b'log'] = True
3200
3198
3201 # we don't merge the first commit when continuing
3199 # we don't merge the first commit when continuing
3202 if not cont:
3200 if not cont:
3203 # perform the graft merge with p1(rev) as 'ancestor'
3201 # perform the graft merge with p1(rev) as 'ancestor'
3204 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3202 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3205 base = ctx.p1() if basectx is None else basectx
3203 base = ctx.p1() if basectx is None else basectx
3206 with ui.configoverride(overrides, b'graft'):
3204 with ui.configoverride(overrides, b'graft'):
3207 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3205 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3208 # report any conflicts
3206 # report any conflicts
3209 if stats.unresolvedcount > 0:
3207 if stats.unresolvedcount > 0:
3210 # write out state for --continue
3208 # write out state for --continue
3211 nodes = [repo[rev].hex() for rev in revs[pos:]]
3209 nodes = [repo[rev].hex() for rev in revs[pos:]]
3212 statedata[b'nodes'] = nodes
3210 statedata[b'nodes'] = nodes
3213 stateversion = 1
3211 stateversion = 1
3214 graftstate.save(stateversion, statedata)
3212 graftstate.save(stateversion, statedata)
3215 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3213 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3216 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3214 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3217 return 1
3215 return 1
3218 else:
3216 else:
3219 cont = False
3217 cont = False
3220
3218
3221 # commit if --no-commit is false
3219 # commit if --no-commit is false
3222 if not opts.get(b'no_commit'):
3220 if not opts.get(b'no_commit'):
3223 node = repo.commit(
3221 node = repo.commit(
3224 text=message, user=user, date=date, extra=extra, editor=editor
3222 text=message, user=user, date=date, extra=extra, editor=editor
3225 )
3223 )
3226 if node is None:
3224 if node is None:
3227 ui.warn(
3225 ui.warn(
3228 _(b'note: graft of %d:%s created no changes to commit\n')
3226 _(b'note: graft of %d:%s created no changes to commit\n')
3229 % (ctx.rev(), ctx)
3227 % (ctx.rev(), ctx)
3230 )
3228 )
3231 # checking that newnodes exist because old state files won't have it
3229 # checking that newnodes exist because old state files won't have it
3232 elif statedata.get(b'newnodes') is not None:
3230 elif statedata.get(b'newnodes') is not None:
3233 statedata[b'newnodes'].append(node)
3231 statedata[b'newnodes'].append(node)
3234
3232
3235 # remove state when we complete successfully
3233 # remove state when we complete successfully
3236 if not opts.get(b'dry_run'):
3234 if not opts.get(b'dry_run'):
3237 graftstate.delete()
3235 graftstate.delete()
3238
3236
3239 return 0
3237 return 0
3240
3238
3241
3239
3242 def _stopgraft(ui, repo, graftstate):
3240 def _stopgraft(ui, repo, graftstate):
3243 """stop the interrupted graft"""
3241 """stop the interrupted graft"""
3244 if not graftstate.exists():
3242 if not graftstate.exists():
3245 raise error.Abort(_(b"no interrupted graft found"))
3243 raise error.Abort(_(b"no interrupted graft found"))
3246 pctx = repo[b'.']
3244 pctx = repo[b'.']
3247 hg.updaterepo(repo, pctx.node(), overwrite=True)
3245 hg.updaterepo(repo, pctx.node(), overwrite=True)
3248 graftstate.delete()
3246 graftstate.delete()
3249 ui.status(_(b"stopped the interrupted graft\n"))
3247 ui.status(_(b"stopped the interrupted graft\n"))
3250 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3248 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3251 return 0
3249 return 0
3252
3250
3253
3251
3254 statemod.addunfinished(
3252 statemod.addunfinished(
3255 b'graft',
3253 b'graft',
3256 fname=b'graftstate',
3254 fname=b'graftstate',
3257 clearable=True,
3255 clearable=True,
3258 stopflag=True,
3256 stopflag=True,
3259 continueflag=True,
3257 continueflag=True,
3260 abortfunc=cmdutil.hgabortgraft,
3258 abortfunc=cmdutil.hgabortgraft,
3261 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3259 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3262 )
3260 )
3263
3261
3264
3262
3265 @command(
3263 @command(
3266 b'grep',
3264 b'grep',
3267 [
3265 [
3268 (b'0', b'print0', None, _(b'end fields with NUL')),
3266 (b'0', b'print0', None, _(b'end fields with NUL')),
3269 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3267 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3270 (
3268 (
3271 b'',
3269 b'',
3272 b'diff',
3270 b'diff',
3273 None,
3271 None,
3274 _(
3272 _(
3275 b'search revision differences for when the pattern was added '
3273 b'search revision differences for when the pattern was added '
3276 b'or removed'
3274 b'or removed'
3277 ),
3275 ),
3278 ),
3276 ),
3279 (b'a', b'text', None, _(b'treat all files as text')),
3277 (b'a', b'text', None, _(b'treat all files as text')),
3280 (
3278 (
3281 b'f',
3279 b'f',
3282 b'follow',
3280 b'follow',
3283 None,
3281 None,
3284 _(
3282 _(
3285 b'follow changeset history,'
3283 b'follow changeset history,'
3286 b' or file history across copies and renames'
3284 b' or file history across copies and renames'
3287 ),
3285 ),
3288 ),
3286 ),
3289 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3287 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3290 (
3288 (
3291 b'l',
3289 b'l',
3292 b'files-with-matches',
3290 b'files-with-matches',
3293 None,
3291 None,
3294 _(b'print only filenames and revisions that match'),
3292 _(b'print only filenames and revisions that match'),
3295 ),
3293 ),
3296 (b'n', b'line-number', None, _(b'print matching line numbers')),
3294 (b'n', b'line-number', None, _(b'print matching line numbers')),
3297 (
3295 (
3298 b'r',
3296 b'r',
3299 b'rev',
3297 b'rev',
3300 [],
3298 [],
3301 _(b'search files changed within revision range'),
3299 _(b'search files changed within revision range'),
3302 _(b'REV'),
3300 _(b'REV'),
3303 ),
3301 ),
3304 (
3302 (
3305 b'',
3303 b'',
3306 b'all-files',
3304 b'all-files',
3307 None,
3305 None,
3308 _(
3306 _(
3309 b'include all files in the changeset while grepping (DEPRECATED)'
3307 b'include all files in the changeset while grepping (DEPRECATED)'
3310 ),
3308 ),
3311 ),
3309 ),
3312 (b'u', b'user', None, _(b'list the author (long with -v)')),
3310 (b'u', b'user', None, _(b'list the author (long with -v)')),
3313 (b'd', b'date', None, _(b'list the date (short with -q)')),
3311 (b'd', b'date', None, _(b'list the date (short with -q)')),
3314 ]
3312 ]
3315 + formatteropts
3313 + formatteropts
3316 + walkopts,
3314 + walkopts,
3317 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3315 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3318 helpcategory=command.CATEGORY_FILE_CONTENTS,
3316 helpcategory=command.CATEGORY_FILE_CONTENTS,
3319 inferrepo=True,
3317 inferrepo=True,
3320 intents={INTENT_READONLY},
3318 intents={INTENT_READONLY},
3321 )
3319 )
3322 def grep(ui, repo, pattern, *pats, **opts):
3320 def grep(ui, repo, pattern, *pats, **opts):
3323 """search for a pattern in specified files
3321 """search for a pattern in specified files
3324
3322
3325 Search the working directory or revision history for a regular
3323 Search the working directory or revision history for a regular
3326 expression in the specified files for the entire repository.
3324 expression in the specified files for the entire repository.
3327
3325
3328 By default, grep searches the repository files in the working
3326 By default, grep searches the repository files in the working
3329 directory and prints the files where it finds a match. To specify
3327 directory and prints the files where it finds a match. To specify
3330 historical revisions instead of the working directory, use the
3328 historical revisions instead of the working directory, use the
3331 --rev flag.
3329 --rev flag.
3332
3330
3333 To search instead historical revision differences that contains a
3331 To search instead historical revision differences that contains a
3334 change in match status ("-" for a match that becomes a non-match,
3332 change in match status ("-" for a match that becomes a non-match,
3335 or "+" for a non-match that becomes a match), use the --diff flag.
3333 or "+" for a non-match that becomes a match), use the --diff flag.
3336
3334
3337 PATTERN can be any Python (roughly Perl-compatible) regular
3335 PATTERN can be any Python (roughly Perl-compatible) regular
3338 expression.
3336 expression.
3339
3337
3340 If no FILEs are specified and the --rev flag isn't supplied, all
3338 If no FILEs are specified and the --rev flag isn't supplied, all
3341 files in the working directory are searched. When using the --rev
3339 files in the working directory are searched. When using the --rev
3342 flag and specifying FILEs, use the --follow argument to also
3340 flag and specifying FILEs, use the --follow argument to also
3343 follow the specified FILEs across renames and copies.
3341 follow the specified FILEs across renames and copies.
3344
3342
3345 .. container:: verbose
3343 .. container:: verbose
3346
3344
3347 Template:
3345 Template:
3348
3346
3349 The following keywords are supported in addition to the common template
3347 The following keywords are supported in addition to the common template
3350 keywords and functions. See also :hg:`help templates`.
3348 keywords and functions. See also :hg:`help templates`.
3351
3349
3352 :change: String. Character denoting insertion ``+`` or removal ``-``.
3350 :change: String. Character denoting insertion ``+`` or removal ``-``.
3353 Available if ``--diff`` is specified.
3351 Available if ``--diff`` is specified.
3354 :lineno: Integer. Line number of the match.
3352 :lineno: Integer. Line number of the match.
3355 :path: String. Repository-absolute path of the file.
3353 :path: String. Repository-absolute path of the file.
3356 :texts: List of text chunks.
3354 :texts: List of text chunks.
3357
3355
3358 And each entry of ``{texts}`` provides the following sub-keywords.
3356 And each entry of ``{texts}`` provides the following sub-keywords.
3359
3357
3360 :matched: Boolean. True if the chunk matches the specified pattern.
3358 :matched: Boolean. True if the chunk matches the specified pattern.
3361 :text: String. Chunk content.
3359 :text: String. Chunk content.
3362
3360
3363 See :hg:`help templates.operators` for the list expansion syntax.
3361 See :hg:`help templates.operators` for the list expansion syntax.
3364
3362
3365 Returns 0 if a match is found, 1 otherwise.
3363 Returns 0 if a match is found, 1 otherwise.
3366
3364
3367 """
3365 """
3368 opts = pycompat.byteskwargs(opts)
3366 opts = pycompat.byteskwargs(opts)
3369 diff = opts.get(b'all') or opts.get(b'diff')
3367 diff = opts.get(b'all') or opts.get(b'diff')
3370 if diff and opts.get(b'all_files'):
3368 if diff and opts.get(b'all_files'):
3371 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3369 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3372 if opts.get(b'all_files') is None and not diff:
3370 if opts.get(b'all_files') is None and not diff:
3373 opts[b'all_files'] = True
3371 opts[b'all_files'] = True
3374 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3372 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3375 all_files = opts.get(b'all_files')
3373 all_files = opts.get(b'all_files')
3376 if plaingrep:
3374 if plaingrep:
3377 opts[b'rev'] = [b'wdir()']
3375 opts[b'rev'] = [b'wdir()']
3378
3376
3379 reflags = re.M
3377 reflags = re.M
3380 if opts.get(b'ignore_case'):
3378 if opts.get(b'ignore_case'):
3381 reflags |= re.I
3379 reflags |= re.I
3382 try:
3380 try:
3383 regexp = util.re.compile(pattern, reflags)
3381 regexp = util.re.compile(pattern, reflags)
3384 except re.error as inst:
3382 except re.error as inst:
3385 ui.warn(
3383 ui.warn(
3386 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3384 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3387 )
3385 )
3388 return 1
3386 return 1
3389 sep, eol = b':', b'\n'
3387 sep, eol = b':', b'\n'
3390 if opts.get(b'print0'):
3388 if opts.get(b'print0'):
3391 sep = eol = b'\0'
3389 sep = eol = b'\0'
3392
3390
3393 getfile = util.lrucachefunc(repo.file)
3391 getfile = util.lrucachefunc(repo.file)
3394
3392
3395 def matchlines(body):
3393 def matchlines(body):
3396 begin = 0
3394 begin = 0
3397 linenum = 0
3395 linenum = 0
3398 while begin < len(body):
3396 while begin < len(body):
3399 match = regexp.search(body, begin)
3397 match = regexp.search(body, begin)
3400 if not match:
3398 if not match:
3401 break
3399 break
3402 mstart, mend = match.span()
3400 mstart, mend = match.span()
3403 linenum += body.count(b'\n', begin, mstart) + 1
3401 linenum += body.count(b'\n', begin, mstart) + 1
3404 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3402 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3405 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3403 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3406 lend = begin - 1
3404 lend = begin - 1
3407 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3405 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3408
3406
3409 class linestate(object):
3407 class linestate(object):
3410 def __init__(self, line, linenum, colstart, colend):
3408 def __init__(self, line, linenum, colstart, colend):
3411 self.line = line
3409 self.line = line
3412 self.linenum = linenum
3410 self.linenum = linenum
3413 self.colstart = colstart
3411 self.colstart = colstart
3414 self.colend = colend
3412 self.colend = colend
3415
3413
3416 def __hash__(self):
3414 def __hash__(self):
3417 return hash((self.linenum, self.line))
3415 return hash((self.linenum, self.line))
3418
3416
3419 def __eq__(self, other):
3417 def __eq__(self, other):
3420 return self.line == other.line
3418 return self.line == other.line
3421
3419
3422 def findpos(self):
3420 def findpos(self):
3423 """Iterate all (start, end) indices of matches"""
3421 """Iterate all (start, end) indices of matches"""
3424 yield self.colstart, self.colend
3422 yield self.colstart, self.colend
3425 p = self.colend
3423 p = self.colend
3426 while p < len(self.line):
3424 while p < len(self.line):
3427 m = regexp.search(self.line, p)
3425 m = regexp.search(self.line, p)
3428 if not m:
3426 if not m:
3429 break
3427 break
3430 yield m.span()
3428 yield m.span()
3431 p = m.end()
3429 p = m.end()
3432
3430
3433 matches = {}
3431 matches = {}
3434 copies = {}
3432 copies = {}
3435
3433
3436 def grepbody(fn, rev, body):
3434 def grepbody(fn, rev, body):
3437 matches[rev].setdefault(fn, [])
3435 matches[rev].setdefault(fn, [])
3438 m = matches[rev][fn]
3436 m = matches[rev][fn]
3439 if body is None:
3437 if body is None:
3440 return
3438 return
3441
3439
3442 for lnum, cstart, cend, line in matchlines(body):
3440 for lnum, cstart, cend, line in matchlines(body):
3443 s = linestate(line, lnum, cstart, cend)
3441 s = linestate(line, lnum, cstart, cend)
3444 m.append(s)
3442 m.append(s)
3445
3443
3446 def difflinestates(a, b):
3444 def difflinestates(a, b):
3447 sm = difflib.SequenceMatcher(None, a, b)
3445 sm = difflib.SequenceMatcher(None, a, b)
3448 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3446 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3449 if tag == 'insert':
3447 if tag == 'insert':
3450 for i in pycompat.xrange(blo, bhi):
3448 for i in pycompat.xrange(blo, bhi):
3451 yield (b'+', b[i])
3449 yield (b'+', b[i])
3452 elif tag == 'delete':
3450 elif tag == 'delete':
3453 for i in pycompat.xrange(alo, ahi):
3451 for i in pycompat.xrange(alo, ahi):
3454 yield (b'-', a[i])
3452 yield (b'-', a[i])
3455 elif tag == 'replace':
3453 elif tag == 'replace':
3456 for i in pycompat.xrange(alo, ahi):
3454 for i in pycompat.xrange(alo, ahi):
3457 yield (b'-', a[i])
3455 yield (b'-', a[i])
3458 for i in pycompat.xrange(blo, bhi):
3456 for i in pycompat.xrange(blo, bhi):
3459 yield (b'+', b[i])
3457 yield (b'+', b[i])
3460
3458
3461 uipathfn = scmutil.getuipathfn(repo)
3459 uipathfn = scmutil.getuipathfn(repo)
3462
3460
3463 def display(fm, fn, ctx, pstates, states):
3461 def display(fm, fn, ctx, pstates, states):
3464 rev = scmutil.intrev(ctx)
3462 rev = scmutil.intrev(ctx)
3465 if fm.isplain():
3463 if fm.isplain():
3466 formatuser = ui.shortuser
3464 formatuser = ui.shortuser
3467 else:
3465 else:
3468 formatuser = pycompat.bytestr
3466 formatuser = pycompat.bytestr
3469 if ui.quiet:
3467 if ui.quiet:
3470 datefmt = b'%Y-%m-%d'
3468 datefmt = b'%Y-%m-%d'
3471 else:
3469 else:
3472 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3470 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3473 found = False
3471 found = False
3474
3472
3475 @util.cachefunc
3473 @util.cachefunc
3476 def binary():
3474 def binary():
3477 flog = getfile(fn)
3475 flog = getfile(fn)
3478 try:
3476 try:
3479 return stringutil.binary(flog.read(ctx.filenode(fn)))
3477 return stringutil.binary(flog.read(ctx.filenode(fn)))
3480 except error.WdirUnsupported:
3478 except error.WdirUnsupported:
3481 return ctx[fn].isbinary()
3479 return ctx[fn].isbinary()
3482
3480
3483 fieldnamemap = {b'linenumber': b'lineno'}
3481 fieldnamemap = {b'linenumber': b'lineno'}
3484 if diff:
3482 if diff:
3485 iter = difflinestates(pstates, states)
3483 iter = difflinestates(pstates, states)
3486 else:
3484 else:
3487 iter = [(b'', l) for l in states]
3485 iter = [(b'', l) for l in states]
3488 for change, l in iter:
3486 for change, l in iter:
3489 fm.startitem()
3487 fm.startitem()
3490 fm.context(ctx=ctx)
3488 fm.context(ctx=ctx)
3491 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3489 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3492 fm.plain(uipathfn(fn), label=b'grep.filename')
3490 fm.plain(uipathfn(fn), label=b'grep.filename')
3493
3491
3494 cols = [
3492 cols = [
3495 (b'rev', b'%d', rev, not plaingrep, b''),
3493 (b'rev', b'%d', rev, not plaingrep, b''),
3496 (
3494 (
3497 b'linenumber',
3495 b'linenumber',
3498 b'%d',
3496 b'%d',
3499 l.linenum,
3497 l.linenum,
3500 opts.get(b'line_number'),
3498 opts.get(b'line_number'),
3501 b'',
3499 b'',
3502 ),
3500 ),
3503 ]
3501 ]
3504 if diff:
3502 if diff:
3505 cols.append(
3503 cols.append(
3506 (
3504 (
3507 b'change',
3505 b'change',
3508 b'%s',
3506 b'%s',
3509 change,
3507 change,
3510 True,
3508 True,
3511 b'grep.inserted '
3509 b'grep.inserted '
3512 if change == b'+'
3510 if change == b'+'
3513 else b'grep.deleted ',
3511 else b'grep.deleted ',
3514 )
3512 )
3515 )
3513 )
3516 cols.extend(
3514 cols.extend(
3517 [
3515 [
3518 (
3516 (
3519 b'user',
3517 b'user',
3520 b'%s',
3518 b'%s',
3521 formatuser(ctx.user()),
3519 formatuser(ctx.user()),
3522 opts.get(b'user'),
3520 opts.get(b'user'),
3523 b'',
3521 b'',
3524 ),
3522 ),
3525 (
3523 (
3526 b'date',
3524 b'date',
3527 b'%s',
3525 b'%s',
3528 fm.formatdate(ctx.date(), datefmt),
3526 fm.formatdate(ctx.date(), datefmt),
3529 opts.get(b'date'),
3527 opts.get(b'date'),
3530 b'',
3528 b'',
3531 ),
3529 ),
3532 ]
3530 ]
3533 )
3531 )
3534 for name, fmt, data, cond, extra_label in cols:
3532 for name, fmt, data, cond, extra_label in cols:
3535 if cond:
3533 if cond:
3536 fm.plain(sep, label=b'grep.sep')
3534 fm.plain(sep, label=b'grep.sep')
3537 field = fieldnamemap.get(name, name)
3535 field = fieldnamemap.get(name, name)
3538 label = extra_label + (b'grep.%s' % name)
3536 label = extra_label + (b'grep.%s' % name)
3539 fm.condwrite(cond, field, fmt, data, label=label)
3537 fm.condwrite(cond, field, fmt, data, label=label)
3540 if not opts.get(b'files_with_matches'):
3538 if not opts.get(b'files_with_matches'):
3541 fm.plain(sep, label=b'grep.sep')
3539 fm.plain(sep, label=b'grep.sep')
3542 if not opts.get(b'text') and binary():
3540 if not opts.get(b'text') and binary():
3543 fm.plain(_(b" Binary file matches"))
3541 fm.plain(_(b" Binary file matches"))
3544 else:
3542 else:
3545 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3543 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3546 fm.plain(eol)
3544 fm.plain(eol)
3547 found = True
3545 found = True
3548 if opts.get(b'files_with_matches'):
3546 if opts.get(b'files_with_matches'):
3549 break
3547 break
3550 return found
3548 return found
3551
3549
3552 def displaymatches(fm, l):
3550 def displaymatches(fm, l):
3553 p = 0
3551 p = 0
3554 for s, e in l.findpos():
3552 for s, e in l.findpos():
3555 if p < s:
3553 if p < s:
3556 fm.startitem()
3554 fm.startitem()
3557 fm.write(b'text', b'%s', l.line[p:s])
3555 fm.write(b'text', b'%s', l.line[p:s])
3558 fm.data(matched=False)
3556 fm.data(matched=False)
3559 fm.startitem()
3557 fm.startitem()
3560 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3558 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3561 fm.data(matched=True)
3559 fm.data(matched=True)
3562 p = e
3560 p = e
3563 if p < len(l.line):
3561 if p < len(l.line):
3564 fm.startitem()
3562 fm.startitem()
3565 fm.write(b'text', b'%s', l.line[p:])
3563 fm.write(b'text', b'%s', l.line[p:])
3566 fm.data(matched=False)
3564 fm.data(matched=False)
3567 fm.end()
3565 fm.end()
3568
3566
3569 skip = set()
3567 skip = set()
3570 revfiles = {}
3568 revfiles = {}
3571 match = scmutil.match(repo[None], pats, opts)
3569 match = scmutil.match(repo[None], pats, opts)
3572 found = False
3570 found = False
3573 follow = opts.get(b'follow')
3571 follow = opts.get(b'follow')
3574
3572
3575 getrenamed = scmutil.getrenamedfn(repo)
3573 getrenamed = scmutil.getrenamedfn(repo)
3576
3574
3577 def get_file_content(filename, filelog, filenode, context, revision):
3575 def get_file_content(filename, filelog, filenode, context, revision):
3578 try:
3576 try:
3579 content = filelog.read(filenode)
3577 content = filelog.read(filenode)
3580 except error.WdirUnsupported:
3578 except error.WdirUnsupported:
3581 content = context[filename].data()
3579 content = context[filename].data()
3582 except error.CensoredNodeError:
3580 except error.CensoredNodeError:
3583 content = None
3581 content = None
3584 ui.warn(
3582 ui.warn(
3585 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3583 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3586 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3584 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3587 )
3585 )
3588 return content
3586 return content
3589
3587
3590 def prep(ctx, fns):
3588 def prep(ctx, fns):
3591 rev = ctx.rev()
3589 rev = ctx.rev()
3592 pctx = ctx.p1()
3590 pctx = ctx.p1()
3593 parent = pctx.rev()
3591 parent = pctx.rev()
3594 matches.setdefault(rev, {})
3592 matches.setdefault(rev, {})
3595 matches.setdefault(parent, {})
3593 matches.setdefault(parent, {})
3596 files = revfiles.setdefault(rev, [])
3594 files = revfiles.setdefault(rev, [])
3597 for fn in fns:
3595 for fn in fns:
3598 flog = getfile(fn)
3596 flog = getfile(fn)
3599 try:
3597 try:
3600 fnode = ctx.filenode(fn)
3598 fnode = ctx.filenode(fn)
3601 except error.LookupError:
3599 except error.LookupError:
3602 continue
3600 continue
3603
3601
3604 copy = None
3602 copy = None
3605 if follow:
3603 if follow:
3606 copy = getrenamed(fn, rev)
3604 copy = getrenamed(fn, rev)
3607 if copy:
3605 if copy:
3608 copies.setdefault(rev, {})[fn] = copy
3606 copies.setdefault(rev, {})[fn] = copy
3609 if fn in skip:
3607 if fn in skip:
3610 skip.add(copy)
3608 skip.add(copy)
3611 if fn in skip:
3609 if fn in skip:
3612 continue
3610 continue
3613 files.append(fn)
3611 files.append(fn)
3614
3612
3615 if fn not in matches[rev]:
3613 if fn not in matches[rev]:
3616 content = get_file_content(fn, flog, fnode, ctx, rev)
3614 content = get_file_content(fn, flog, fnode, ctx, rev)
3617 grepbody(fn, rev, content)
3615 grepbody(fn, rev, content)
3618
3616
3619 pfn = copy or fn
3617 pfn = copy or fn
3620 if pfn not in matches[parent]:
3618 if pfn not in matches[parent]:
3621 try:
3619 try:
3622 pfnode = pctx.filenode(pfn)
3620 pfnode = pctx.filenode(pfn)
3623 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3621 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3624 grepbody(pfn, parent, pcontent)
3622 grepbody(pfn, parent, pcontent)
3625 except error.LookupError:
3623 except error.LookupError:
3626 pass
3624 pass
3627
3625
3628 ui.pager(b'grep')
3626 ui.pager(b'grep')
3629 fm = ui.formatter(b'grep', opts)
3627 fm = ui.formatter(b'grep', opts)
3630 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3628 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3631 rev = ctx.rev()
3629 rev = ctx.rev()
3632 parent = ctx.p1().rev()
3630 parent = ctx.p1().rev()
3633 for fn in sorted(revfiles.get(rev, [])):
3631 for fn in sorted(revfiles.get(rev, [])):
3634 states = matches[rev][fn]
3632 states = matches[rev][fn]
3635 copy = copies.get(rev, {}).get(fn)
3633 copy = copies.get(rev, {}).get(fn)
3636 if fn in skip:
3634 if fn in skip:
3637 if copy:
3635 if copy:
3638 skip.add(copy)
3636 skip.add(copy)
3639 continue
3637 continue
3640 pstates = matches.get(parent, {}).get(copy or fn, [])
3638 pstates = matches.get(parent, {}).get(copy or fn, [])
3641 if pstates or states:
3639 if pstates or states:
3642 r = display(fm, fn, ctx, pstates, states)
3640 r = display(fm, fn, ctx, pstates, states)
3643 found = found or r
3641 found = found or r
3644 if r and not diff and not all_files:
3642 if r and not diff and not all_files:
3645 skip.add(fn)
3643 skip.add(fn)
3646 if copy:
3644 if copy:
3647 skip.add(copy)
3645 skip.add(copy)
3648 del revfiles[rev]
3646 del revfiles[rev]
3649 # We will keep the matches dict for the duration of the window
3647 # We will keep the matches dict for the duration of the window
3650 # clear the matches dict once the window is over
3648 # clear the matches dict once the window is over
3651 if not revfiles:
3649 if not revfiles:
3652 matches.clear()
3650 matches.clear()
3653 fm.end()
3651 fm.end()
3654
3652
3655 return not found
3653 return not found
3656
3654
3657
3655
3658 @command(
3656 @command(
3659 b'heads',
3657 b'heads',
3660 [
3658 [
3661 (
3659 (
3662 b'r',
3660 b'r',
3663 b'rev',
3661 b'rev',
3664 b'',
3662 b'',
3665 _(b'show only heads which are descendants of STARTREV'),
3663 _(b'show only heads which are descendants of STARTREV'),
3666 _(b'STARTREV'),
3664 _(b'STARTREV'),
3667 ),
3665 ),
3668 (b't', b'topo', False, _(b'show topological heads only')),
3666 (b't', b'topo', False, _(b'show topological heads only')),
3669 (
3667 (
3670 b'a',
3668 b'a',
3671 b'active',
3669 b'active',
3672 False,
3670 False,
3673 _(b'show active branchheads only (DEPRECATED)'),
3671 _(b'show active branchheads only (DEPRECATED)'),
3674 ),
3672 ),
3675 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3673 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3676 ]
3674 ]
3677 + templateopts,
3675 + templateopts,
3678 _(b'[-ct] [-r STARTREV] [REV]...'),
3676 _(b'[-ct] [-r STARTREV] [REV]...'),
3679 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3677 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3680 intents={INTENT_READONLY},
3678 intents={INTENT_READONLY},
3681 )
3679 )
3682 def heads(ui, repo, *branchrevs, **opts):
3680 def heads(ui, repo, *branchrevs, **opts):
3683 """show branch heads
3681 """show branch heads
3684
3682
3685 With no arguments, show all open branch heads in the repository.
3683 With no arguments, show all open branch heads in the repository.
3686 Branch heads are changesets that have no descendants on the
3684 Branch heads are changesets that have no descendants on the
3687 same branch. They are where development generally takes place and
3685 same branch. They are where development generally takes place and
3688 are the usual targets for update and merge operations.
3686 are the usual targets for update and merge operations.
3689
3687
3690 If one or more REVs are given, only open branch heads on the
3688 If one or more REVs are given, only open branch heads on the
3691 branches associated with the specified changesets are shown. This
3689 branches associated with the specified changesets are shown. This
3692 means that you can use :hg:`heads .` to see the heads on the
3690 means that you can use :hg:`heads .` to see the heads on the
3693 currently checked-out branch.
3691 currently checked-out branch.
3694
3692
3695 If -c/--closed is specified, also show branch heads marked closed
3693 If -c/--closed is specified, also show branch heads marked closed
3696 (see :hg:`commit --close-branch`).
3694 (see :hg:`commit --close-branch`).
3697
3695
3698 If STARTREV is specified, only those heads that are descendants of
3696 If STARTREV is specified, only those heads that are descendants of
3699 STARTREV will be displayed.
3697 STARTREV will be displayed.
3700
3698
3701 If -t/--topo is specified, named branch mechanics will be ignored and only
3699 If -t/--topo is specified, named branch mechanics will be ignored and only
3702 topological heads (changesets with no children) will be shown.
3700 topological heads (changesets with no children) will be shown.
3703
3701
3704 Returns 0 if matching heads are found, 1 if not.
3702 Returns 0 if matching heads are found, 1 if not.
3705 """
3703 """
3706
3704
3707 opts = pycompat.byteskwargs(opts)
3705 opts = pycompat.byteskwargs(opts)
3708 start = None
3706 start = None
3709 rev = opts.get(b'rev')
3707 rev = opts.get(b'rev')
3710 if rev:
3708 if rev:
3711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3709 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3712 start = scmutil.revsingle(repo, rev, None).node()
3710 start = scmutil.revsingle(repo, rev, None).node()
3713
3711
3714 if opts.get(b'topo'):
3712 if opts.get(b'topo'):
3715 heads = [repo[h] for h in repo.heads(start)]
3713 heads = [repo[h] for h in repo.heads(start)]
3716 else:
3714 else:
3717 heads = []
3715 heads = []
3718 for branch in repo.branchmap():
3716 for branch in repo.branchmap():
3719 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3717 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3720 heads = [repo[h] for h in heads]
3718 heads = [repo[h] for h in heads]
3721
3719
3722 if branchrevs:
3720 if branchrevs:
3723 branches = {
3721 branches = {
3724 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3722 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3725 }
3723 }
3726 heads = [h for h in heads if h.branch() in branches]
3724 heads = [h for h in heads if h.branch() in branches]
3727
3725
3728 if opts.get(b'active') and branchrevs:
3726 if opts.get(b'active') and branchrevs:
3729 dagheads = repo.heads(start)
3727 dagheads = repo.heads(start)
3730 heads = [h for h in heads if h.node() in dagheads]
3728 heads = [h for h in heads if h.node() in dagheads]
3731
3729
3732 if branchrevs:
3730 if branchrevs:
3733 haveheads = {h.branch() for h in heads}
3731 haveheads = {h.branch() for h in heads}
3734 if branches - haveheads:
3732 if branches - haveheads:
3735 headless = b', '.join(b for b in branches - haveheads)
3733 headless = b', '.join(b for b in branches - haveheads)
3736 msg = _(b'no open branch heads found on branches %s')
3734 msg = _(b'no open branch heads found on branches %s')
3737 if opts.get(b'rev'):
3735 if opts.get(b'rev'):
3738 msg += _(b' (started at %s)') % opts[b'rev']
3736 msg += _(b' (started at %s)') % opts[b'rev']
3739 ui.warn((msg + b'\n') % headless)
3737 ui.warn((msg + b'\n') % headless)
3740
3738
3741 if not heads:
3739 if not heads:
3742 return 1
3740 return 1
3743
3741
3744 ui.pager(b'heads')
3742 ui.pager(b'heads')
3745 heads = sorted(heads, key=lambda x: -(x.rev()))
3743 heads = sorted(heads, key=lambda x: -(x.rev()))
3746 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3744 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3747 for ctx in heads:
3745 for ctx in heads:
3748 displayer.show(ctx)
3746 displayer.show(ctx)
3749 displayer.close()
3747 displayer.close()
3750
3748
3751
3749
3752 @command(
3750 @command(
3753 b'help',
3751 b'help',
3754 [
3752 [
3755 (b'e', b'extension', None, _(b'show only help for extensions')),
3753 (b'e', b'extension', None, _(b'show only help for extensions')),
3756 (b'c', b'command', None, _(b'show only help for commands')),
3754 (b'c', b'command', None, _(b'show only help for commands')),
3757 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3755 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3758 (
3756 (
3759 b's',
3757 b's',
3760 b'system',
3758 b'system',
3761 [],
3759 [],
3762 _(b'show help for specific platform(s)'),
3760 _(b'show help for specific platform(s)'),
3763 _(b'PLATFORM'),
3761 _(b'PLATFORM'),
3764 ),
3762 ),
3765 ],
3763 ],
3766 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3764 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3767 helpcategory=command.CATEGORY_HELP,
3765 helpcategory=command.CATEGORY_HELP,
3768 norepo=True,
3766 norepo=True,
3769 intents={INTENT_READONLY},
3767 intents={INTENT_READONLY},
3770 )
3768 )
3771 def help_(ui, name=None, **opts):
3769 def help_(ui, name=None, **opts):
3772 """show help for a given topic or a help overview
3770 """show help for a given topic or a help overview
3773
3771
3774 With no arguments, print a list of commands with short help messages.
3772 With no arguments, print a list of commands with short help messages.
3775
3773
3776 Given a topic, extension, or command name, print help for that
3774 Given a topic, extension, or command name, print help for that
3777 topic.
3775 topic.
3778
3776
3779 Returns 0 if successful.
3777 Returns 0 if successful.
3780 """
3778 """
3781
3779
3782 keep = opts.get('system') or []
3780 keep = opts.get('system') or []
3783 if len(keep) == 0:
3781 if len(keep) == 0:
3784 if pycompat.sysplatform.startswith(b'win'):
3782 if pycompat.sysplatform.startswith(b'win'):
3785 keep.append(b'windows')
3783 keep.append(b'windows')
3786 elif pycompat.sysplatform == b'OpenVMS':
3784 elif pycompat.sysplatform == b'OpenVMS':
3787 keep.append(b'vms')
3785 keep.append(b'vms')
3788 elif pycompat.sysplatform == b'plan9':
3786 elif pycompat.sysplatform == b'plan9':
3789 keep.append(b'plan9')
3787 keep.append(b'plan9')
3790 else:
3788 else:
3791 keep.append(b'unix')
3789 keep.append(b'unix')
3792 keep.append(pycompat.sysplatform.lower())
3790 keep.append(pycompat.sysplatform.lower())
3793 if ui.verbose:
3791 if ui.verbose:
3794 keep.append(b'verbose')
3792 keep.append(b'verbose')
3795
3793
3796 commands = sys.modules[__name__]
3794 commands = sys.modules[__name__]
3797 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3795 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3798 ui.pager(b'help')
3796 ui.pager(b'help')
3799 ui.write(formatted)
3797 ui.write(formatted)
3800
3798
3801
3799
3802 @command(
3800 @command(
3803 b'identify|id',
3801 b'identify|id',
3804 [
3802 [
3805 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3803 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3806 (b'n', b'num', None, _(b'show local revision number')),
3804 (b'n', b'num', None, _(b'show local revision number')),
3807 (b'i', b'id', None, _(b'show global revision id')),
3805 (b'i', b'id', None, _(b'show global revision id')),
3808 (b'b', b'branch', None, _(b'show branch')),
3806 (b'b', b'branch', None, _(b'show branch')),
3809 (b't', b'tags', None, _(b'show tags')),
3807 (b't', b'tags', None, _(b'show tags')),
3810 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3808 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3811 ]
3809 ]
3812 + remoteopts
3810 + remoteopts
3813 + formatteropts,
3811 + formatteropts,
3814 _(b'[-nibtB] [-r REV] [SOURCE]'),
3812 _(b'[-nibtB] [-r REV] [SOURCE]'),
3815 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3813 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3816 optionalrepo=True,
3814 optionalrepo=True,
3817 intents={INTENT_READONLY},
3815 intents={INTENT_READONLY},
3818 )
3816 )
3819 def identify(
3817 def identify(
3820 ui,
3818 ui,
3821 repo,
3819 repo,
3822 source=None,
3820 source=None,
3823 rev=None,
3821 rev=None,
3824 num=None,
3822 num=None,
3825 id=None,
3823 id=None,
3826 branch=None,
3824 branch=None,
3827 tags=None,
3825 tags=None,
3828 bookmarks=None,
3826 bookmarks=None,
3829 **opts
3827 **opts
3830 ):
3828 ):
3831 """identify the working directory or specified revision
3829 """identify the working directory or specified revision
3832
3830
3833 Print a summary identifying the repository state at REV using one or
3831 Print a summary identifying the repository state at REV using one or
3834 two parent hash identifiers, followed by a "+" if the working
3832 two parent hash identifiers, followed by a "+" if the working
3835 directory has uncommitted changes, the branch name (if not default),
3833 directory has uncommitted changes, the branch name (if not default),
3836 a list of tags, and a list of bookmarks.
3834 a list of tags, and a list of bookmarks.
3837
3835
3838 When REV is not given, print a summary of the current state of the
3836 When REV is not given, print a summary of the current state of the
3839 repository including the working directory. Specify -r. to get information
3837 repository including the working directory. Specify -r. to get information
3840 of the working directory parent without scanning uncommitted changes.
3838 of the working directory parent without scanning uncommitted changes.
3841
3839
3842 Specifying a path to a repository root or Mercurial bundle will
3840 Specifying a path to a repository root or Mercurial bundle will
3843 cause lookup to operate on that repository/bundle.
3841 cause lookup to operate on that repository/bundle.
3844
3842
3845 .. container:: verbose
3843 .. container:: verbose
3846
3844
3847 Template:
3845 Template:
3848
3846
3849 The following keywords are supported in addition to the common template
3847 The following keywords are supported in addition to the common template
3850 keywords and functions. See also :hg:`help templates`.
3848 keywords and functions. See also :hg:`help templates`.
3851
3849
3852 :dirty: String. Character ``+`` denoting if the working directory has
3850 :dirty: String. Character ``+`` denoting if the working directory has
3853 uncommitted changes.
3851 uncommitted changes.
3854 :id: String. One or two nodes, optionally followed by ``+``.
3852 :id: String. One or two nodes, optionally followed by ``+``.
3855 :parents: List of strings. Parent nodes of the changeset.
3853 :parents: List of strings. Parent nodes of the changeset.
3856
3854
3857 Examples:
3855 Examples:
3858
3856
3859 - generate a build identifier for the working directory::
3857 - generate a build identifier for the working directory::
3860
3858
3861 hg id --id > build-id.dat
3859 hg id --id > build-id.dat
3862
3860
3863 - find the revision corresponding to a tag::
3861 - find the revision corresponding to a tag::
3864
3862
3865 hg id -n -r 1.3
3863 hg id -n -r 1.3
3866
3864
3867 - check the most recent revision of a remote repository::
3865 - check the most recent revision of a remote repository::
3868
3866
3869 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3867 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3870
3868
3871 See :hg:`log` for generating more information about specific revisions,
3869 See :hg:`log` for generating more information about specific revisions,
3872 including full hash identifiers.
3870 including full hash identifiers.
3873
3871
3874 Returns 0 if successful.
3872 Returns 0 if successful.
3875 """
3873 """
3876
3874
3877 opts = pycompat.byteskwargs(opts)
3875 opts = pycompat.byteskwargs(opts)
3878 if not repo and not source:
3876 if not repo and not source:
3879 raise error.Abort(
3877 raise error.Abort(
3880 _(b"there is no Mercurial repository here (.hg not found)")
3878 _(b"there is no Mercurial repository here (.hg not found)")
3881 )
3879 )
3882
3880
3883 default = not (num or id or branch or tags or bookmarks)
3881 default = not (num or id or branch or tags or bookmarks)
3884 output = []
3882 output = []
3885 revs = []
3883 revs = []
3886
3884
3887 if source:
3885 if source:
3888 source, branches = hg.parseurl(ui.expandpath(source))
3886 source, branches = hg.parseurl(ui.expandpath(source))
3889 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3887 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3890 repo = peer.local()
3888 repo = peer.local()
3891 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3889 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3892
3890
3893 fm = ui.formatter(b'identify', opts)
3891 fm = ui.formatter(b'identify', opts)
3894 fm.startitem()
3892 fm.startitem()
3895
3893
3896 if not repo:
3894 if not repo:
3897 if num or branch or tags:
3895 if num or branch or tags:
3898 raise error.Abort(
3896 raise error.Abort(
3899 _(b"can't query remote revision number, branch, or tags")
3897 _(b"can't query remote revision number, branch, or tags")
3900 )
3898 )
3901 if not rev and revs:
3899 if not rev and revs:
3902 rev = revs[0]
3900 rev = revs[0]
3903 if not rev:
3901 if not rev:
3904 rev = b"tip"
3902 rev = b"tip"
3905
3903
3906 remoterev = peer.lookup(rev)
3904 remoterev = peer.lookup(rev)
3907 hexrev = fm.hexfunc(remoterev)
3905 hexrev = fm.hexfunc(remoterev)
3908 if default or id:
3906 if default or id:
3909 output = [hexrev]
3907 output = [hexrev]
3910 fm.data(id=hexrev)
3908 fm.data(id=hexrev)
3911
3909
3912 @util.cachefunc
3910 @util.cachefunc
3913 def getbms():
3911 def getbms():
3914 bms = []
3912 bms = []
3915
3913
3916 if b'bookmarks' in peer.listkeys(b'namespaces'):
3914 if b'bookmarks' in peer.listkeys(b'namespaces'):
3917 hexremoterev = hex(remoterev)
3915 hexremoterev = hex(remoterev)
3918 bms = [
3916 bms = [
3919 bm
3917 bm
3920 for bm, bmr in pycompat.iteritems(
3918 for bm, bmr in pycompat.iteritems(
3921 peer.listkeys(b'bookmarks')
3919 peer.listkeys(b'bookmarks')
3922 )
3920 )
3923 if bmr == hexremoterev
3921 if bmr == hexremoterev
3924 ]
3922 ]
3925
3923
3926 return sorted(bms)
3924 return sorted(bms)
3927
3925
3928 if fm.isplain():
3926 if fm.isplain():
3929 if bookmarks:
3927 if bookmarks:
3930 output.extend(getbms())
3928 output.extend(getbms())
3931 elif default and not ui.quiet:
3929 elif default and not ui.quiet:
3932 # multiple bookmarks for a single parent separated by '/'
3930 # multiple bookmarks for a single parent separated by '/'
3933 bm = b'/'.join(getbms())
3931 bm = b'/'.join(getbms())
3934 if bm:
3932 if bm:
3935 output.append(bm)
3933 output.append(bm)
3936 else:
3934 else:
3937 fm.data(node=hex(remoterev))
3935 fm.data(node=hex(remoterev))
3938 if bookmarks or b'bookmarks' in fm.datahint():
3936 if bookmarks or b'bookmarks' in fm.datahint():
3939 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3937 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3940 else:
3938 else:
3941 if rev:
3939 if rev:
3942 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3940 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3943 ctx = scmutil.revsingle(repo, rev, None)
3941 ctx = scmutil.revsingle(repo, rev, None)
3944
3942
3945 if ctx.rev() is None:
3943 if ctx.rev() is None:
3946 ctx = repo[None]
3944 ctx = repo[None]
3947 parents = ctx.parents()
3945 parents = ctx.parents()
3948 taglist = []
3946 taglist = []
3949 for p in parents:
3947 for p in parents:
3950 taglist.extend(p.tags())
3948 taglist.extend(p.tags())
3951
3949
3952 dirty = b""
3950 dirty = b""
3953 if ctx.dirty(missing=True, merge=False, branch=False):
3951 if ctx.dirty(missing=True, merge=False, branch=False):
3954 dirty = b'+'
3952 dirty = b'+'
3955 fm.data(dirty=dirty)
3953 fm.data(dirty=dirty)
3956
3954
3957 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3955 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3958 if default or id:
3956 if default or id:
3959 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3957 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3960 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3958 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3961
3959
3962 if num:
3960 if num:
3963 numoutput = [b"%d" % p.rev() for p in parents]
3961 numoutput = [b"%d" % p.rev() for p in parents]
3964 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3962 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3965
3963
3966 fm.data(
3964 fm.data(
3967 parents=fm.formatlist(
3965 parents=fm.formatlist(
3968 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3966 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3969 )
3967 )
3970 )
3968 )
3971 else:
3969 else:
3972 hexoutput = fm.hexfunc(ctx.node())
3970 hexoutput = fm.hexfunc(ctx.node())
3973 if default or id:
3971 if default or id:
3974 output = [hexoutput]
3972 output = [hexoutput]
3975 fm.data(id=hexoutput)
3973 fm.data(id=hexoutput)
3976
3974
3977 if num:
3975 if num:
3978 output.append(pycompat.bytestr(ctx.rev()))
3976 output.append(pycompat.bytestr(ctx.rev()))
3979 taglist = ctx.tags()
3977 taglist = ctx.tags()
3980
3978
3981 if default and not ui.quiet:
3979 if default and not ui.quiet:
3982 b = ctx.branch()
3980 b = ctx.branch()
3983 if b != b'default':
3981 if b != b'default':
3984 output.append(b"(%s)" % b)
3982 output.append(b"(%s)" % b)
3985
3983
3986 # multiple tags for a single parent separated by '/'
3984 # multiple tags for a single parent separated by '/'
3987 t = b'/'.join(taglist)
3985 t = b'/'.join(taglist)
3988 if t:
3986 if t:
3989 output.append(t)
3987 output.append(t)
3990
3988
3991 # multiple bookmarks for a single parent separated by '/'
3989 # multiple bookmarks for a single parent separated by '/'
3992 bm = b'/'.join(ctx.bookmarks())
3990 bm = b'/'.join(ctx.bookmarks())
3993 if bm:
3991 if bm:
3994 output.append(bm)
3992 output.append(bm)
3995 else:
3993 else:
3996 if branch:
3994 if branch:
3997 output.append(ctx.branch())
3995 output.append(ctx.branch())
3998
3996
3999 if tags:
3997 if tags:
4000 output.extend(taglist)
3998 output.extend(taglist)
4001
3999
4002 if bookmarks:
4000 if bookmarks:
4003 output.extend(ctx.bookmarks())
4001 output.extend(ctx.bookmarks())
4004
4002
4005 fm.data(node=ctx.hex())
4003 fm.data(node=ctx.hex())
4006 fm.data(branch=ctx.branch())
4004 fm.data(branch=ctx.branch())
4007 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4005 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4008 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4006 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4009 fm.context(ctx=ctx)
4007 fm.context(ctx=ctx)
4010
4008
4011 fm.plain(b"%s\n" % b' '.join(output))
4009 fm.plain(b"%s\n" % b' '.join(output))
4012 fm.end()
4010 fm.end()
4013
4011
4014
4012
4015 @command(
4013 @command(
4016 b'import|patch',
4014 b'import|patch',
4017 [
4015 [
4018 (
4016 (
4019 b'p',
4017 b'p',
4020 b'strip',
4018 b'strip',
4021 1,
4019 1,
4022 _(
4020 _(
4023 b'directory strip option for patch. This has the same '
4021 b'directory strip option for patch. This has the same '
4024 b'meaning as the corresponding patch option'
4022 b'meaning as the corresponding patch option'
4025 ),
4023 ),
4026 _(b'NUM'),
4024 _(b'NUM'),
4027 ),
4025 ),
4028 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4026 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4029 (b'', b'secret', None, _(b'use the secret phase for committing')),
4027 (b'', b'secret', None, _(b'use the secret phase for committing')),
4030 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4028 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4031 (
4029 (
4032 b'f',
4030 b'f',
4033 b'force',
4031 b'force',
4034 None,
4032 None,
4035 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4033 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4036 ),
4034 ),
4037 (
4035 (
4038 b'',
4036 b'',
4039 b'no-commit',
4037 b'no-commit',
4040 None,
4038 None,
4041 _(b"don't commit, just update the working directory"),
4039 _(b"don't commit, just update the working directory"),
4042 ),
4040 ),
4043 (
4041 (
4044 b'',
4042 b'',
4045 b'bypass',
4043 b'bypass',
4046 None,
4044 None,
4047 _(b"apply patch without touching the working directory"),
4045 _(b"apply patch without touching the working directory"),
4048 ),
4046 ),
4049 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4047 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4050 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4048 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4051 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4049 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4052 (
4050 (
4053 b'',
4051 b'',
4054 b'import-branch',
4052 b'import-branch',
4055 None,
4053 None,
4056 _(b'use any branch information in patch (implied by --exact)'),
4054 _(b'use any branch information in patch (implied by --exact)'),
4057 ),
4055 ),
4058 ]
4056 ]
4059 + commitopts
4057 + commitopts
4060 + commitopts2
4058 + commitopts2
4061 + similarityopts,
4059 + similarityopts,
4062 _(b'[OPTION]... PATCH...'),
4060 _(b'[OPTION]... PATCH...'),
4063 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4061 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4064 )
4062 )
4065 def import_(ui, repo, patch1=None, *patches, **opts):
4063 def import_(ui, repo, patch1=None, *patches, **opts):
4066 """import an ordered set of patches
4064 """import an ordered set of patches
4067
4065
4068 Import a list of patches and commit them individually (unless
4066 Import a list of patches and commit them individually (unless
4069 --no-commit is specified).
4067 --no-commit is specified).
4070
4068
4071 To read a patch from standard input (stdin), use "-" as the patch
4069 To read a patch from standard input (stdin), use "-" as the patch
4072 name. If a URL is specified, the patch will be downloaded from
4070 name. If a URL is specified, the patch will be downloaded from
4073 there.
4071 there.
4074
4072
4075 Import first applies changes to the working directory (unless
4073 Import first applies changes to the working directory (unless
4076 --bypass is specified), import will abort if there are outstanding
4074 --bypass is specified), import will abort if there are outstanding
4077 changes.
4075 changes.
4078
4076
4079 Use --bypass to apply and commit patches directly to the
4077 Use --bypass to apply and commit patches directly to the
4080 repository, without affecting the working directory. Without
4078 repository, without affecting the working directory. Without
4081 --exact, patches will be applied on top of the working directory
4079 --exact, patches will be applied on top of the working directory
4082 parent revision.
4080 parent revision.
4083
4081
4084 You can import a patch straight from a mail message. Even patches
4082 You can import a patch straight from a mail message. Even patches
4085 as attachments work (to use the body part, it must have type
4083 as attachments work (to use the body part, it must have type
4086 text/plain or text/x-patch). From and Subject headers of email
4084 text/plain or text/x-patch). From and Subject headers of email
4087 message are used as default committer and commit message. All
4085 message are used as default committer and commit message. All
4088 text/plain body parts before first diff are added to the commit
4086 text/plain body parts before first diff are added to the commit
4089 message.
4087 message.
4090
4088
4091 If the imported patch was generated by :hg:`export`, user and
4089 If the imported patch was generated by :hg:`export`, user and
4092 description from patch override values from message headers and
4090 description from patch override values from message headers and
4093 body. Values given on command line with -m/--message and -u/--user
4091 body. Values given on command line with -m/--message and -u/--user
4094 override these.
4092 override these.
4095
4093
4096 If --exact is specified, import will set the working directory to
4094 If --exact is specified, import will set the working directory to
4097 the parent of each patch before applying it, and will abort if the
4095 the parent of each patch before applying it, and will abort if the
4098 resulting changeset has a different ID than the one recorded in
4096 resulting changeset has a different ID than the one recorded in
4099 the patch. This will guard against various ways that portable
4097 the patch. This will guard against various ways that portable
4100 patch formats and mail systems might fail to transfer Mercurial
4098 patch formats and mail systems might fail to transfer Mercurial
4101 data or metadata. See :hg:`bundle` for lossless transmission.
4099 data or metadata. See :hg:`bundle` for lossless transmission.
4102
4100
4103 Use --partial to ensure a changeset will be created from the patch
4101 Use --partial to ensure a changeset will be created from the patch
4104 even if some hunks fail to apply. Hunks that fail to apply will be
4102 even if some hunks fail to apply. Hunks that fail to apply will be
4105 written to a <target-file>.rej file. Conflicts can then be resolved
4103 written to a <target-file>.rej file. Conflicts can then be resolved
4106 by hand before :hg:`commit --amend` is run to update the created
4104 by hand before :hg:`commit --amend` is run to update the created
4107 changeset. This flag exists to let people import patches that
4105 changeset. This flag exists to let people import patches that
4108 partially apply without losing the associated metadata (author,
4106 partially apply without losing the associated metadata (author,
4109 date, description, ...).
4107 date, description, ...).
4110
4108
4111 .. note::
4109 .. note::
4112
4110
4113 When no hunks apply cleanly, :hg:`import --partial` will create
4111 When no hunks apply cleanly, :hg:`import --partial` will create
4114 an empty changeset, importing only the patch metadata.
4112 an empty changeset, importing only the patch metadata.
4115
4113
4116 With -s/--similarity, hg will attempt to discover renames and
4114 With -s/--similarity, hg will attempt to discover renames and
4117 copies in the patch in the same way as :hg:`addremove`.
4115 copies in the patch in the same way as :hg:`addremove`.
4118
4116
4119 It is possible to use external patch programs to perform the patch
4117 It is possible to use external patch programs to perform the patch
4120 by setting the ``ui.patch`` configuration option. For the default
4118 by setting the ``ui.patch`` configuration option. For the default
4121 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4119 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4122 See :hg:`help config` for more information about configuration
4120 See :hg:`help config` for more information about configuration
4123 files and how to use these options.
4121 files and how to use these options.
4124
4122
4125 See :hg:`help dates` for a list of formats valid for -d/--date.
4123 See :hg:`help dates` for a list of formats valid for -d/--date.
4126
4124
4127 .. container:: verbose
4125 .. container:: verbose
4128
4126
4129 Examples:
4127 Examples:
4130
4128
4131 - import a traditional patch from a website and detect renames::
4129 - import a traditional patch from a website and detect renames::
4132
4130
4133 hg import -s 80 http://example.com/bugfix.patch
4131 hg import -s 80 http://example.com/bugfix.patch
4134
4132
4135 - import a changeset from an hgweb server::
4133 - import a changeset from an hgweb server::
4136
4134
4137 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4135 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4138
4136
4139 - import all the patches in an Unix-style mbox::
4137 - import all the patches in an Unix-style mbox::
4140
4138
4141 hg import incoming-patches.mbox
4139 hg import incoming-patches.mbox
4142
4140
4143 - import patches from stdin::
4141 - import patches from stdin::
4144
4142
4145 hg import -
4143 hg import -
4146
4144
4147 - attempt to exactly restore an exported changeset (not always
4145 - attempt to exactly restore an exported changeset (not always
4148 possible)::
4146 possible)::
4149
4147
4150 hg import --exact proposed-fix.patch
4148 hg import --exact proposed-fix.patch
4151
4149
4152 - use an external tool to apply a patch which is too fuzzy for
4150 - use an external tool to apply a patch which is too fuzzy for
4153 the default internal tool.
4151 the default internal tool.
4154
4152
4155 hg import --config ui.patch="patch --merge" fuzzy.patch
4153 hg import --config ui.patch="patch --merge" fuzzy.patch
4156
4154
4157 - change the default fuzzing from 2 to a less strict 7
4155 - change the default fuzzing from 2 to a less strict 7
4158
4156
4159 hg import --config ui.fuzz=7 fuzz.patch
4157 hg import --config ui.fuzz=7 fuzz.patch
4160
4158
4161 Returns 0 on success, 1 on partial success (see --partial).
4159 Returns 0 on success, 1 on partial success (see --partial).
4162 """
4160 """
4163
4161
4164 opts = pycompat.byteskwargs(opts)
4162 opts = pycompat.byteskwargs(opts)
4165 if not patch1:
4163 if not patch1:
4166 raise error.Abort(_(b'need at least one patch to import'))
4164 raise error.Abort(_(b'need at least one patch to import'))
4167
4165
4168 patches = (patch1,) + patches
4166 patches = (patch1,) + patches
4169
4167
4170 date = opts.get(b'date')
4168 date = opts.get(b'date')
4171 if date:
4169 if date:
4172 opts[b'date'] = dateutil.parsedate(date)
4170 opts[b'date'] = dateutil.parsedate(date)
4173
4171
4174 exact = opts.get(b'exact')
4172 exact = opts.get(b'exact')
4175 update = not opts.get(b'bypass')
4173 update = not opts.get(b'bypass')
4176 if not update and opts.get(b'no_commit'):
4174 if not update and opts.get(b'no_commit'):
4177 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4175 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4178 if opts.get(b'secret') and opts.get(b'no_commit'):
4176 if opts.get(b'secret') and opts.get(b'no_commit'):
4179 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4177 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4180 try:
4178 try:
4181 sim = float(opts.get(b'similarity') or 0)
4179 sim = float(opts.get(b'similarity') or 0)
4182 except ValueError:
4180 except ValueError:
4183 raise error.Abort(_(b'similarity must be a number'))
4181 raise error.Abort(_(b'similarity must be a number'))
4184 if sim < 0 or sim > 100:
4182 if sim < 0 or sim > 100:
4185 raise error.Abort(_(b'similarity must be between 0 and 100'))
4183 raise error.Abort(_(b'similarity must be between 0 and 100'))
4186 if sim and not update:
4184 if sim and not update:
4187 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4185 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4188 if exact:
4186 if exact:
4189 if opts.get(b'edit'):
4187 if opts.get(b'edit'):
4190 raise error.Abort(_(b'cannot use --exact with --edit'))
4188 raise error.Abort(_(b'cannot use --exact with --edit'))
4191 if opts.get(b'prefix'):
4189 if opts.get(b'prefix'):
4192 raise error.Abort(_(b'cannot use --exact with --prefix'))
4190 raise error.Abort(_(b'cannot use --exact with --prefix'))
4193
4191
4194 base = opts[b"base"]
4192 base = opts[b"base"]
4195 msgs = []
4193 msgs = []
4196 ret = 0
4194 ret = 0
4197
4195
4198 with repo.wlock():
4196 with repo.wlock():
4199 if update:
4197 if update:
4200 cmdutil.checkunfinished(repo)
4198 cmdutil.checkunfinished(repo)
4201 if exact or not opts.get(b'force'):
4199 if exact or not opts.get(b'force'):
4202 cmdutil.bailifchanged(repo)
4200 cmdutil.bailifchanged(repo)
4203
4201
4204 if not opts.get(b'no_commit'):
4202 if not opts.get(b'no_commit'):
4205 lock = repo.lock
4203 lock = repo.lock
4206 tr = lambda: repo.transaction(b'import')
4204 tr = lambda: repo.transaction(b'import')
4207 dsguard = util.nullcontextmanager
4205 dsguard = util.nullcontextmanager
4208 else:
4206 else:
4209 lock = util.nullcontextmanager
4207 lock = util.nullcontextmanager
4210 tr = util.nullcontextmanager
4208 tr = util.nullcontextmanager
4211 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4209 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4212 with lock(), tr(), dsguard():
4210 with lock(), tr(), dsguard():
4213 parents = repo[None].parents()
4211 parents = repo[None].parents()
4214 for patchurl in patches:
4212 for patchurl in patches:
4215 if patchurl == b'-':
4213 if patchurl == b'-':
4216 ui.status(_(b'applying patch from stdin\n'))
4214 ui.status(_(b'applying patch from stdin\n'))
4217 patchfile = ui.fin
4215 patchfile = ui.fin
4218 patchurl = b'stdin' # for error message
4216 patchurl = b'stdin' # for error message
4219 else:
4217 else:
4220 patchurl = os.path.join(base, patchurl)
4218 patchurl = os.path.join(base, patchurl)
4221 ui.status(_(b'applying %s\n') % patchurl)
4219 ui.status(_(b'applying %s\n') % patchurl)
4222 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4220 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4223
4221
4224 haspatch = False
4222 haspatch = False
4225 for hunk in patch.split(patchfile):
4223 for hunk in patch.split(patchfile):
4226 with patch.extract(ui, hunk) as patchdata:
4224 with patch.extract(ui, hunk) as patchdata:
4227 msg, node, rej = cmdutil.tryimportone(
4225 msg, node, rej = cmdutil.tryimportone(
4228 ui, repo, patchdata, parents, opts, msgs, hg.clean
4226 ui, repo, patchdata, parents, opts, msgs, hg.clean
4229 )
4227 )
4230 if msg:
4228 if msg:
4231 haspatch = True
4229 haspatch = True
4232 ui.note(msg + b'\n')
4230 ui.note(msg + b'\n')
4233 if update or exact:
4231 if update or exact:
4234 parents = repo[None].parents()
4232 parents = repo[None].parents()
4235 else:
4233 else:
4236 parents = [repo[node]]
4234 parents = [repo[node]]
4237 if rej:
4235 if rej:
4238 ui.write_err(_(b"patch applied partially\n"))
4236 ui.write_err(_(b"patch applied partially\n"))
4239 ui.write_err(
4237 ui.write_err(
4240 _(
4238 _(
4241 b"(fix the .rej files and run "
4239 b"(fix the .rej files and run "
4242 b"`hg commit --amend`)\n"
4240 b"`hg commit --amend`)\n"
4243 )
4241 )
4244 )
4242 )
4245 ret = 1
4243 ret = 1
4246 break
4244 break
4247
4245
4248 if not haspatch:
4246 if not haspatch:
4249 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4247 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4250
4248
4251 if msgs:
4249 if msgs:
4252 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4250 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4253 return ret
4251 return ret
4254
4252
4255
4253
4256 @command(
4254 @command(
4257 b'incoming|in',
4255 b'incoming|in',
4258 [
4256 [
4259 (
4257 (
4260 b'f',
4258 b'f',
4261 b'force',
4259 b'force',
4262 None,
4260 None,
4263 _(b'run even if remote repository is unrelated'),
4261 _(b'run even if remote repository is unrelated'),
4264 ),
4262 ),
4265 (b'n', b'newest-first', None, _(b'show newest record first')),
4263 (b'n', b'newest-first', None, _(b'show newest record first')),
4266 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4264 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4267 (
4265 (
4268 b'r',
4266 b'r',
4269 b'rev',
4267 b'rev',
4270 [],
4268 [],
4271 _(b'a remote changeset intended to be added'),
4269 _(b'a remote changeset intended to be added'),
4272 _(b'REV'),
4270 _(b'REV'),
4273 ),
4271 ),
4274 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4272 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4275 (
4273 (
4276 b'b',
4274 b'b',
4277 b'branch',
4275 b'branch',
4278 [],
4276 [],
4279 _(b'a specific branch you would like to pull'),
4277 _(b'a specific branch you would like to pull'),
4280 _(b'BRANCH'),
4278 _(b'BRANCH'),
4281 ),
4279 ),
4282 ]
4280 ]
4283 + logopts
4281 + logopts
4284 + remoteopts
4282 + remoteopts
4285 + subrepoopts,
4283 + subrepoopts,
4286 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4284 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4287 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4285 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4288 )
4286 )
4289 def incoming(ui, repo, source=b"default", **opts):
4287 def incoming(ui, repo, source=b"default", **opts):
4290 """show new changesets found in source
4288 """show new changesets found in source
4291
4289
4292 Show new changesets found in the specified path/URL or the default
4290 Show new changesets found in the specified path/URL or the default
4293 pull location. These are the changesets that would have been pulled
4291 pull location. These are the changesets that would have been pulled
4294 by :hg:`pull` at the time you issued this command.
4292 by :hg:`pull` at the time you issued this command.
4295
4293
4296 See pull for valid source format details.
4294 See pull for valid source format details.
4297
4295
4298 .. container:: verbose
4296 .. container:: verbose
4299
4297
4300 With -B/--bookmarks, the result of bookmark comparison between
4298 With -B/--bookmarks, the result of bookmark comparison between
4301 local and remote repositories is displayed. With -v/--verbose,
4299 local and remote repositories is displayed. With -v/--verbose,
4302 status is also displayed for each bookmark like below::
4300 status is also displayed for each bookmark like below::
4303
4301
4304 BM1 01234567890a added
4302 BM1 01234567890a added
4305 BM2 1234567890ab advanced
4303 BM2 1234567890ab advanced
4306 BM3 234567890abc diverged
4304 BM3 234567890abc diverged
4307 BM4 34567890abcd changed
4305 BM4 34567890abcd changed
4308
4306
4309 The action taken locally when pulling depends on the
4307 The action taken locally when pulling depends on the
4310 status of each bookmark:
4308 status of each bookmark:
4311
4309
4312 :``added``: pull will create it
4310 :``added``: pull will create it
4313 :``advanced``: pull will update it
4311 :``advanced``: pull will update it
4314 :``diverged``: pull will create a divergent bookmark
4312 :``diverged``: pull will create a divergent bookmark
4315 :``changed``: result depends on remote changesets
4313 :``changed``: result depends on remote changesets
4316
4314
4317 From the point of view of pulling behavior, bookmark
4315 From the point of view of pulling behavior, bookmark
4318 existing only in the remote repository are treated as ``added``,
4316 existing only in the remote repository are treated as ``added``,
4319 even if it is in fact locally deleted.
4317 even if it is in fact locally deleted.
4320
4318
4321 .. container:: verbose
4319 .. container:: verbose
4322
4320
4323 For remote repository, using --bundle avoids downloading the
4321 For remote repository, using --bundle avoids downloading the
4324 changesets twice if the incoming is followed by a pull.
4322 changesets twice if the incoming is followed by a pull.
4325
4323
4326 Examples:
4324 Examples:
4327
4325
4328 - show incoming changes with patches and full description::
4326 - show incoming changes with patches and full description::
4329
4327
4330 hg incoming -vp
4328 hg incoming -vp
4331
4329
4332 - show incoming changes excluding merges, store a bundle::
4330 - show incoming changes excluding merges, store a bundle::
4333
4331
4334 hg in -vpM --bundle incoming.hg
4332 hg in -vpM --bundle incoming.hg
4335 hg pull incoming.hg
4333 hg pull incoming.hg
4336
4334
4337 - briefly list changes inside a bundle::
4335 - briefly list changes inside a bundle::
4338
4336
4339 hg in changes.hg -T "{desc|firstline}\\n"
4337 hg in changes.hg -T "{desc|firstline}\\n"
4340
4338
4341 Returns 0 if there are incoming changes, 1 otherwise.
4339 Returns 0 if there are incoming changes, 1 otherwise.
4342 """
4340 """
4343 opts = pycompat.byteskwargs(opts)
4341 opts = pycompat.byteskwargs(opts)
4344 if opts.get(b'graph'):
4342 if opts.get(b'graph'):
4345 logcmdutil.checkunsupportedgraphflags([], opts)
4343 logcmdutil.checkunsupportedgraphflags([], opts)
4346
4344
4347 def display(other, chlist, displayer):
4345 def display(other, chlist, displayer):
4348 revdag = logcmdutil.graphrevs(other, chlist, opts)
4346 revdag = logcmdutil.graphrevs(other, chlist, opts)
4349 logcmdutil.displaygraph(
4347 logcmdutil.displaygraph(
4350 ui, repo, revdag, displayer, graphmod.asciiedges
4348 ui, repo, revdag, displayer, graphmod.asciiedges
4351 )
4349 )
4352
4350
4353 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4351 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4354 return 0
4352 return 0
4355
4353
4356 if opts.get(b'bundle') and opts.get(b'subrepos'):
4354 if opts.get(b'bundle') and opts.get(b'subrepos'):
4357 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4355 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4358
4356
4359 if opts.get(b'bookmarks'):
4357 if opts.get(b'bookmarks'):
4360 source, branches = hg.parseurl(
4358 source, branches = hg.parseurl(
4361 ui.expandpath(source), opts.get(b'branch')
4359 ui.expandpath(source), opts.get(b'branch')
4362 )
4360 )
4363 other = hg.peer(repo, opts, source)
4361 other = hg.peer(repo, opts, source)
4364 if b'bookmarks' not in other.listkeys(b'namespaces'):
4362 if b'bookmarks' not in other.listkeys(b'namespaces'):
4365 ui.warn(_(b"remote doesn't support bookmarks\n"))
4363 ui.warn(_(b"remote doesn't support bookmarks\n"))
4366 return 0
4364 return 0
4367 ui.pager(b'incoming')
4365 ui.pager(b'incoming')
4368 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4366 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4369 return bookmarks.incoming(ui, repo, other)
4367 return bookmarks.incoming(ui, repo, other)
4370
4368
4371 repo._subtoppath = ui.expandpath(source)
4369 repo._subtoppath = ui.expandpath(source)
4372 try:
4370 try:
4373 return hg.incoming(ui, repo, source, opts)
4371 return hg.incoming(ui, repo, source, opts)
4374 finally:
4372 finally:
4375 del repo._subtoppath
4373 del repo._subtoppath
4376
4374
4377
4375
4378 @command(
4376 @command(
4379 b'init',
4377 b'init',
4380 remoteopts,
4378 remoteopts,
4381 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4379 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4382 helpcategory=command.CATEGORY_REPO_CREATION,
4380 helpcategory=command.CATEGORY_REPO_CREATION,
4383 helpbasic=True,
4381 helpbasic=True,
4384 norepo=True,
4382 norepo=True,
4385 )
4383 )
4386 def init(ui, dest=b".", **opts):
4384 def init(ui, dest=b".", **opts):
4387 """create a new repository in the given directory
4385 """create a new repository in the given directory
4388
4386
4389 Initialize a new repository in the given directory. If the given
4387 Initialize a new repository in the given directory. If the given
4390 directory does not exist, it will be created.
4388 directory does not exist, it will be created.
4391
4389
4392 If no directory is given, the current directory is used.
4390 If no directory is given, the current directory is used.
4393
4391
4394 It is possible to specify an ``ssh://`` URL as the destination.
4392 It is possible to specify an ``ssh://`` URL as the destination.
4395 See :hg:`help urls` for more information.
4393 See :hg:`help urls` for more information.
4396
4394
4397 Returns 0 on success.
4395 Returns 0 on success.
4398 """
4396 """
4399 opts = pycompat.byteskwargs(opts)
4397 opts = pycompat.byteskwargs(opts)
4400 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4398 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4401
4399
4402
4400
4403 @command(
4401 @command(
4404 b'locate',
4402 b'locate',
4405 [
4403 [
4406 (
4404 (
4407 b'r',
4405 b'r',
4408 b'rev',
4406 b'rev',
4409 b'',
4407 b'',
4410 _(b'search the repository as it is in REV'),
4408 _(b'search the repository as it is in REV'),
4411 _(b'REV'),
4409 _(b'REV'),
4412 ),
4410 ),
4413 (
4411 (
4414 b'0',
4412 b'0',
4415 b'print0',
4413 b'print0',
4416 None,
4414 None,
4417 _(b'end filenames with NUL, for use with xargs'),
4415 _(b'end filenames with NUL, for use with xargs'),
4418 ),
4416 ),
4419 (
4417 (
4420 b'f',
4418 b'f',
4421 b'fullpath',
4419 b'fullpath',
4422 None,
4420 None,
4423 _(b'print complete paths from the filesystem root'),
4421 _(b'print complete paths from the filesystem root'),
4424 ),
4422 ),
4425 ]
4423 ]
4426 + walkopts,
4424 + walkopts,
4427 _(b'[OPTION]... [PATTERN]...'),
4425 _(b'[OPTION]... [PATTERN]...'),
4428 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4426 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4429 )
4427 )
4430 def locate(ui, repo, *pats, **opts):
4428 def locate(ui, repo, *pats, **opts):
4431 """locate files matching specific patterns (DEPRECATED)
4429 """locate files matching specific patterns (DEPRECATED)
4432
4430
4433 Print files under Mercurial control in the working directory whose
4431 Print files under Mercurial control in the working directory whose
4434 names match the given patterns.
4432 names match the given patterns.
4435
4433
4436 By default, this command searches all directories in the working
4434 By default, this command searches all directories in the working
4437 directory. To search just the current directory and its
4435 directory. To search just the current directory and its
4438 subdirectories, use "--include .".
4436 subdirectories, use "--include .".
4439
4437
4440 If no patterns are given to match, this command prints the names
4438 If no patterns are given to match, this command prints the names
4441 of all files under Mercurial control in the working directory.
4439 of all files under Mercurial control in the working directory.
4442
4440
4443 If you want to feed the output of this command into the "xargs"
4441 If you want to feed the output of this command into the "xargs"
4444 command, use the -0 option to both this command and "xargs". This
4442 command, use the -0 option to both this command and "xargs". This
4445 will avoid the problem of "xargs" treating single filenames that
4443 will avoid the problem of "xargs" treating single filenames that
4446 contain whitespace as multiple filenames.
4444 contain whitespace as multiple filenames.
4447
4445
4448 See :hg:`help files` for a more versatile command.
4446 See :hg:`help files` for a more versatile command.
4449
4447
4450 Returns 0 if a match is found, 1 otherwise.
4448 Returns 0 if a match is found, 1 otherwise.
4451 """
4449 """
4452 opts = pycompat.byteskwargs(opts)
4450 opts = pycompat.byteskwargs(opts)
4453 if opts.get(b'print0'):
4451 if opts.get(b'print0'):
4454 end = b'\0'
4452 end = b'\0'
4455 else:
4453 else:
4456 end = b'\n'
4454 end = b'\n'
4457 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4455 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4458
4456
4459 ret = 1
4457 ret = 1
4460 m = scmutil.match(
4458 m = scmutil.match(
4461 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4459 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4462 )
4460 )
4463
4461
4464 ui.pager(b'locate')
4462 ui.pager(b'locate')
4465 if ctx.rev() is None:
4463 if ctx.rev() is None:
4466 # When run on the working copy, "locate" includes removed files, so
4464 # When run on the working copy, "locate" includes removed files, so
4467 # we get the list of files from the dirstate.
4465 # we get the list of files from the dirstate.
4468 filesgen = sorted(repo.dirstate.matches(m))
4466 filesgen = sorted(repo.dirstate.matches(m))
4469 else:
4467 else:
4470 filesgen = ctx.matches(m)
4468 filesgen = ctx.matches(m)
4471 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4469 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4472 for abs in filesgen:
4470 for abs in filesgen:
4473 if opts.get(b'fullpath'):
4471 if opts.get(b'fullpath'):
4474 ui.write(repo.wjoin(abs), end)
4472 ui.write(repo.wjoin(abs), end)
4475 else:
4473 else:
4476 ui.write(uipathfn(abs), end)
4474 ui.write(uipathfn(abs), end)
4477 ret = 0
4475 ret = 0
4478
4476
4479 return ret
4477 return ret
4480
4478
4481
4479
4482 @command(
4480 @command(
4483 b'log|history',
4481 b'log|history',
4484 [
4482 [
4485 (
4483 (
4486 b'f',
4484 b'f',
4487 b'follow',
4485 b'follow',
4488 None,
4486 None,
4489 _(
4487 _(
4490 b'follow changeset history, or file history across copies and renames'
4488 b'follow changeset history, or file history across copies and renames'
4491 ),
4489 ),
4492 ),
4490 ),
4493 (
4491 (
4494 b'',
4492 b'',
4495 b'follow-first',
4493 b'follow-first',
4496 None,
4494 None,
4497 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4495 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4498 ),
4496 ),
4499 (
4497 (
4500 b'd',
4498 b'd',
4501 b'date',
4499 b'date',
4502 b'',
4500 b'',
4503 _(b'show revisions matching date spec'),
4501 _(b'show revisions matching date spec'),
4504 _(b'DATE'),
4502 _(b'DATE'),
4505 ),
4503 ),
4506 (b'C', b'copies', None, _(b'show copied files')),
4504 (b'C', b'copies', None, _(b'show copied files')),
4507 (
4505 (
4508 b'k',
4506 b'k',
4509 b'keyword',
4507 b'keyword',
4510 [],
4508 [],
4511 _(b'do case-insensitive search for a given text'),
4509 _(b'do case-insensitive search for a given text'),
4512 _(b'TEXT'),
4510 _(b'TEXT'),
4513 ),
4511 ),
4514 (
4512 (
4515 b'r',
4513 b'r',
4516 b'rev',
4514 b'rev',
4517 [],
4515 [],
4518 _(b'show the specified revision or revset'),
4516 _(b'show the specified revision or revset'),
4519 _(b'REV'),
4517 _(b'REV'),
4520 ),
4518 ),
4521 (
4519 (
4522 b'L',
4520 b'L',
4523 b'line-range',
4521 b'line-range',
4524 [],
4522 [],
4525 _(b'follow line range of specified file (EXPERIMENTAL)'),
4523 _(b'follow line range of specified file (EXPERIMENTAL)'),
4526 _(b'FILE,RANGE'),
4524 _(b'FILE,RANGE'),
4527 ),
4525 ),
4528 (
4526 (
4529 b'',
4527 b'',
4530 b'removed',
4528 b'removed',
4531 None,
4529 None,
4532 _(b'include revisions where files were removed'),
4530 _(b'include revisions where files were removed'),
4533 ),
4531 ),
4534 (
4532 (
4535 b'm',
4533 b'm',
4536 b'only-merges',
4534 b'only-merges',
4537 None,
4535 None,
4538 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4536 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4539 ),
4537 ),
4540 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4538 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4541 (
4539 (
4542 b'',
4540 b'',
4543 b'only-branch',
4541 b'only-branch',
4544 [],
4542 [],
4545 _(
4543 _(
4546 b'show only changesets within the given named branch (DEPRECATED)'
4544 b'show only changesets within the given named branch (DEPRECATED)'
4547 ),
4545 ),
4548 _(b'BRANCH'),
4546 _(b'BRANCH'),
4549 ),
4547 ),
4550 (
4548 (
4551 b'b',
4549 b'b',
4552 b'branch',
4550 b'branch',
4553 [],
4551 [],
4554 _(b'show changesets within the given named branch'),
4552 _(b'show changesets within the given named branch'),
4555 _(b'BRANCH'),
4553 _(b'BRANCH'),
4556 ),
4554 ),
4557 (
4555 (
4558 b'P',
4556 b'P',
4559 b'prune',
4557 b'prune',
4560 [],
4558 [],
4561 _(b'do not display revision or any of its ancestors'),
4559 _(b'do not display revision or any of its ancestors'),
4562 _(b'REV'),
4560 _(b'REV'),
4563 ),
4561 ),
4564 ]
4562 ]
4565 + logopts
4563 + logopts
4566 + walkopts,
4564 + walkopts,
4567 _(b'[OPTION]... [FILE]'),
4565 _(b'[OPTION]... [FILE]'),
4568 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4566 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4569 helpbasic=True,
4567 helpbasic=True,
4570 inferrepo=True,
4568 inferrepo=True,
4571 intents={INTENT_READONLY},
4569 intents={INTENT_READONLY},
4572 )
4570 )
4573 def log(ui, repo, *pats, **opts):
4571 def log(ui, repo, *pats, **opts):
4574 """show revision history of entire repository or files
4572 """show revision history of entire repository or files
4575
4573
4576 Print the revision history of the specified files or the entire
4574 Print the revision history of the specified files or the entire
4577 project.
4575 project.
4578
4576
4579 If no revision range is specified, the default is ``tip:0`` unless
4577 If no revision range is specified, the default is ``tip:0`` unless
4580 --follow is set, in which case the working directory parent is
4578 --follow is set, in which case the working directory parent is
4581 used as the starting revision.
4579 used as the starting revision.
4582
4580
4583 File history is shown without following rename or copy history of
4581 File history is shown without following rename or copy history of
4584 files. Use -f/--follow with a filename to follow history across
4582 files. Use -f/--follow with a filename to follow history across
4585 renames and copies. --follow without a filename will only show
4583 renames and copies. --follow without a filename will only show
4586 ancestors of the starting revision.
4584 ancestors of the starting revision.
4587
4585
4588 By default this command prints revision number and changeset id,
4586 By default this command prints revision number and changeset id,
4589 tags, non-trivial parents, user, date and time, and a summary for
4587 tags, non-trivial parents, user, date and time, and a summary for
4590 each commit. When the -v/--verbose switch is used, the list of
4588 each commit. When the -v/--verbose switch is used, the list of
4591 changed files and full commit message are shown.
4589 changed files and full commit message are shown.
4592
4590
4593 With --graph the revisions are shown as an ASCII art DAG with the most
4591 With --graph the revisions are shown as an ASCII art DAG with the most
4594 recent changeset at the top.
4592 recent changeset at the top.
4595 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4593 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4596 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4594 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4597 changeset from the lines below is a parent of the 'o' merge on the same
4595 changeset from the lines below is a parent of the 'o' merge on the same
4598 line.
4596 line.
4599 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4597 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4600 of a '|' indicates one or more revisions in a path are omitted.
4598 of a '|' indicates one or more revisions in a path are omitted.
4601
4599
4602 .. container:: verbose
4600 .. container:: verbose
4603
4601
4604 Use -L/--line-range FILE,M:N options to follow the history of lines
4602 Use -L/--line-range FILE,M:N options to follow the history of lines
4605 from M to N in FILE. With -p/--patch only diff hunks affecting
4603 from M to N in FILE. With -p/--patch only diff hunks affecting
4606 specified line range will be shown. This option requires --follow;
4604 specified line range will be shown. This option requires --follow;
4607 it can be specified multiple times. Currently, this option is not
4605 it can be specified multiple times. Currently, this option is not
4608 compatible with --graph. This option is experimental.
4606 compatible with --graph. This option is experimental.
4609
4607
4610 .. note::
4608 .. note::
4611
4609
4612 :hg:`log --patch` may generate unexpected diff output for merge
4610 :hg:`log --patch` may generate unexpected diff output for merge
4613 changesets, as it will only compare the merge changeset against
4611 changesets, as it will only compare the merge changeset against
4614 its first parent. Also, only files different from BOTH parents
4612 its first parent. Also, only files different from BOTH parents
4615 will appear in files:.
4613 will appear in files:.
4616
4614
4617 .. note::
4615 .. note::
4618
4616
4619 For performance reasons, :hg:`log FILE` may omit duplicate changes
4617 For performance reasons, :hg:`log FILE` may omit duplicate changes
4620 made on branches and will not show removals or mode changes. To
4618 made on branches and will not show removals or mode changes. To
4621 see all such changes, use the --removed switch.
4619 see all such changes, use the --removed switch.
4622
4620
4623 .. container:: verbose
4621 .. container:: verbose
4624
4622
4625 .. note::
4623 .. note::
4626
4624
4627 The history resulting from -L/--line-range options depends on diff
4625 The history resulting from -L/--line-range options depends on diff
4628 options; for instance if white-spaces are ignored, respective changes
4626 options; for instance if white-spaces are ignored, respective changes
4629 with only white-spaces in specified line range will not be listed.
4627 with only white-spaces in specified line range will not be listed.
4630
4628
4631 .. container:: verbose
4629 .. container:: verbose
4632
4630
4633 Some examples:
4631 Some examples:
4634
4632
4635 - changesets with full descriptions and file lists::
4633 - changesets with full descriptions and file lists::
4636
4634
4637 hg log -v
4635 hg log -v
4638
4636
4639 - changesets ancestral to the working directory::
4637 - changesets ancestral to the working directory::
4640
4638
4641 hg log -f
4639 hg log -f
4642
4640
4643 - last 10 commits on the current branch::
4641 - last 10 commits on the current branch::
4644
4642
4645 hg log -l 10 -b .
4643 hg log -l 10 -b .
4646
4644
4647 - changesets showing all modifications of a file, including removals::
4645 - changesets showing all modifications of a file, including removals::
4648
4646
4649 hg log --removed file.c
4647 hg log --removed file.c
4650
4648
4651 - all changesets that touch a directory, with diffs, excluding merges::
4649 - all changesets that touch a directory, with diffs, excluding merges::
4652
4650
4653 hg log -Mp lib/
4651 hg log -Mp lib/
4654
4652
4655 - all revision numbers that match a keyword::
4653 - all revision numbers that match a keyword::
4656
4654
4657 hg log -k bug --template "{rev}\\n"
4655 hg log -k bug --template "{rev}\\n"
4658
4656
4659 - the full hash identifier of the working directory parent::
4657 - the full hash identifier of the working directory parent::
4660
4658
4661 hg log -r . --template "{node}\\n"
4659 hg log -r . --template "{node}\\n"
4662
4660
4663 - list available log templates::
4661 - list available log templates::
4664
4662
4665 hg log -T list
4663 hg log -T list
4666
4664
4667 - check if a given changeset is included in a tagged release::
4665 - check if a given changeset is included in a tagged release::
4668
4666
4669 hg log -r "a21ccf and ancestor(1.9)"
4667 hg log -r "a21ccf and ancestor(1.9)"
4670
4668
4671 - find all changesets by some user in a date range::
4669 - find all changesets by some user in a date range::
4672
4670
4673 hg log -k alice -d "may 2008 to jul 2008"
4671 hg log -k alice -d "may 2008 to jul 2008"
4674
4672
4675 - summary of all changesets after the last tag::
4673 - summary of all changesets after the last tag::
4676
4674
4677 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4675 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4678
4676
4679 - changesets touching lines 13 to 23 for file.c::
4677 - changesets touching lines 13 to 23 for file.c::
4680
4678
4681 hg log -L file.c,13:23
4679 hg log -L file.c,13:23
4682
4680
4683 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4681 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4684 main.c with patch::
4682 main.c with patch::
4685
4683
4686 hg log -L file.c,13:23 -L main.c,2:6 -p
4684 hg log -L file.c,13:23 -L main.c,2:6 -p
4687
4685
4688 See :hg:`help dates` for a list of formats valid for -d/--date.
4686 See :hg:`help dates` for a list of formats valid for -d/--date.
4689
4687
4690 See :hg:`help revisions` for more about specifying and ordering
4688 See :hg:`help revisions` for more about specifying and ordering
4691 revisions.
4689 revisions.
4692
4690
4693 See :hg:`help templates` for more about pre-packaged styles and
4691 See :hg:`help templates` for more about pre-packaged styles and
4694 specifying custom templates. The default template used by the log
4692 specifying custom templates. The default template used by the log
4695 command can be customized via the ``ui.logtemplate`` configuration
4693 command can be customized via the ``ui.logtemplate`` configuration
4696 setting.
4694 setting.
4697
4695
4698 Returns 0 on success.
4696 Returns 0 on success.
4699
4697
4700 """
4698 """
4701 opts = pycompat.byteskwargs(opts)
4699 opts = pycompat.byteskwargs(opts)
4702 linerange = opts.get(b'line_range')
4700 linerange = opts.get(b'line_range')
4703
4701
4704 if linerange and not opts.get(b'follow'):
4702 if linerange and not opts.get(b'follow'):
4705 raise error.Abort(_(b'--line-range requires --follow'))
4703 raise error.Abort(_(b'--line-range requires --follow'))
4706
4704
4707 if linerange and pats:
4705 if linerange and pats:
4708 # TODO: take pats as patterns with no line-range filter
4706 # TODO: take pats as patterns with no line-range filter
4709 raise error.Abort(
4707 raise error.Abort(
4710 _(b'FILE arguments are not compatible with --line-range option')
4708 _(b'FILE arguments are not compatible with --line-range option')
4711 )
4709 )
4712
4710
4713 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4711 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4714 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4712 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4715 if linerange:
4713 if linerange:
4716 # TODO: should follow file history from logcmdutil._initialrevs(),
4714 # TODO: should follow file history from logcmdutil._initialrevs(),
4717 # then filter the result by logcmdutil._makerevset() and --limit
4715 # then filter the result by logcmdutil._makerevset() and --limit
4718 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4716 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4719
4717
4720 getcopies = None
4718 getcopies = None
4721 if opts.get(b'copies'):
4719 if opts.get(b'copies'):
4722 endrev = None
4720 endrev = None
4723 if revs:
4721 if revs:
4724 endrev = revs.max() + 1
4722 endrev = revs.max() + 1
4725 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4723 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4726
4724
4727 ui.pager(b'log')
4725 ui.pager(b'log')
4728 displayer = logcmdutil.changesetdisplayer(
4726 displayer = logcmdutil.changesetdisplayer(
4729 ui, repo, opts, differ, buffered=True
4727 ui, repo, opts, differ, buffered=True
4730 )
4728 )
4731 if opts.get(b'graph'):
4729 if opts.get(b'graph'):
4732 displayfn = logcmdutil.displaygraphrevs
4730 displayfn = logcmdutil.displaygraphrevs
4733 else:
4731 else:
4734 displayfn = logcmdutil.displayrevs
4732 displayfn = logcmdutil.displayrevs
4735 displayfn(ui, repo, revs, displayer, getcopies)
4733 displayfn(ui, repo, revs, displayer, getcopies)
4736
4734
4737
4735
4738 @command(
4736 @command(
4739 b'manifest',
4737 b'manifest',
4740 [
4738 [
4741 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4739 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4742 (b'', b'all', False, _(b"list files from all revisions")),
4740 (b'', b'all', False, _(b"list files from all revisions")),
4743 ]
4741 ]
4744 + formatteropts,
4742 + formatteropts,
4745 _(b'[-r REV]'),
4743 _(b'[-r REV]'),
4746 helpcategory=command.CATEGORY_MAINTENANCE,
4744 helpcategory=command.CATEGORY_MAINTENANCE,
4747 intents={INTENT_READONLY},
4745 intents={INTENT_READONLY},
4748 )
4746 )
4749 def manifest(ui, repo, node=None, rev=None, **opts):
4747 def manifest(ui, repo, node=None, rev=None, **opts):
4750 """output the current or given revision of the project manifest
4748 """output the current or given revision of the project manifest
4751
4749
4752 Print a list of version controlled files for the given revision.
4750 Print a list of version controlled files for the given revision.
4753 If no revision is given, the first parent of the working directory
4751 If no revision is given, the first parent of the working directory
4754 is used, or the null revision if no revision is checked out.
4752 is used, or the null revision if no revision is checked out.
4755
4753
4756 With -v, print file permissions, symlink and executable bits.
4754 With -v, print file permissions, symlink and executable bits.
4757 With --debug, print file revision hashes.
4755 With --debug, print file revision hashes.
4758
4756
4759 If option --all is specified, the list of all files from all revisions
4757 If option --all is specified, the list of all files from all revisions
4760 is printed. This includes deleted and renamed files.
4758 is printed. This includes deleted and renamed files.
4761
4759
4762 Returns 0 on success.
4760 Returns 0 on success.
4763 """
4761 """
4764 opts = pycompat.byteskwargs(opts)
4762 opts = pycompat.byteskwargs(opts)
4765 fm = ui.formatter(b'manifest', opts)
4763 fm = ui.formatter(b'manifest', opts)
4766
4764
4767 if opts.get(b'all'):
4765 if opts.get(b'all'):
4768 if rev or node:
4766 if rev or node:
4769 raise error.Abort(_(b"can't specify a revision with --all"))
4767 raise error.Abort(_(b"can't specify a revision with --all"))
4770
4768
4771 res = set()
4769 res = set()
4772 for rev in repo:
4770 for rev in repo:
4773 ctx = repo[rev]
4771 ctx = repo[rev]
4774 res |= set(ctx.files())
4772 res |= set(ctx.files())
4775
4773
4776 ui.pager(b'manifest')
4774 ui.pager(b'manifest')
4777 for f in sorted(res):
4775 for f in sorted(res):
4778 fm.startitem()
4776 fm.startitem()
4779 fm.write(b"path", b'%s\n', f)
4777 fm.write(b"path", b'%s\n', f)
4780 fm.end()
4778 fm.end()
4781 return
4779 return
4782
4780
4783 if rev and node:
4781 if rev and node:
4784 raise error.Abort(_(b"please specify just one revision"))
4782 raise error.Abort(_(b"please specify just one revision"))
4785
4783
4786 if not node:
4784 if not node:
4787 node = rev
4785 node = rev
4788
4786
4789 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4787 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4790 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4788 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4791 if node:
4789 if node:
4792 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4790 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4793 ctx = scmutil.revsingle(repo, node)
4791 ctx = scmutil.revsingle(repo, node)
4794 mf = ctx.manifest()
4792 mf = ctx.manifest()
4795 ui.pager(b'manifest')
4793 ui.pager(b'manifest')
4796 for f in ctx:
4794 for f in ctx:
4797 fm.startitem()
4795 fm.startitem()
4798 fm.context(ctx=ctx)
4796 fm.context(ctx=ctx)
4799 fl = ctx[f].flags()
4797 fl = ctx[f].flags()
4800 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4798 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4801 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4799 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4802 fm.write(b'path', b'%s\n', f)
4800 fm.write(b'path', b'%s\n', f)
4803 fm.end()
4801 fm.end()
4804
4802
4805
4803
4806 @command(
4804 @command(
4807 b'merge',
4805 b'merge',
4808 [
4806 [
4809 (
4807 (
4810 b'f',
4808 b'f',
4811 b'force',
4809 b'force',
4812 None,
4810 None,
4813 _(b'force a merge including outstanding changes (DEPRECATED)'),
4811 _(b'force a merge including outstanding changes (DEPRECATED)'),
4814 ),
4812 ),
4815 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4813 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4816 (
4814 (
4817 b'P',
4815 b'P',
4818 b'preview',
4816 b'preview',
4819 None,
4817 None,
4820 _(b'review revisions to merge (no merge is performed)'),
4818 _(b'review revisions to merge (no merge is performed)'),
4821 ),
4819 ),
4822 (b'', b'abort', None, _(b'abort the ongoing merge')),
4820 (b'', b'abort', None, _(b'abort the ongoing merge')),
4823 ]
4821 ]
4824 + mergetoolopts,
4822 + mergetoolopts,
4825 _(b'[-P] [[-r] REV]'),
4823 _(b'[-P] [[-r] REV]'),
4826 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4824 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4827 helpbasic=True,
4825 helpbasic=True,
4828 )
4826 )
4829 def merge(ui, repo, node=None, **opts):
4827 def merge(ui, repo, node=None, **opts):
4830 """merge another revision into working directory
4828 """merge another revision into working directory
4831
4829
4832 The current working directory is updated with all changes made in
4830 The current working directory is updated with all changes made in
4833 the requested revision since the last common predecessor revision.
4831 the requested revision since the last common predecessor revision.
4834
4832
4835 Files that changed between either parent are marked as changed for
4833 Files that changed between either parent are marked as changed for
4836 the next commit and a commit must be performed before any further
4834 the next commit and a commit must be performed before any further
4837 updates to the repository are allowed. The next commit will have
4835 updates to the repository are allowed. The next commit will have
4838 two parents.
4836 two parents.
4839
4837
4840 ``--tool`` can be used to specify the merge tool used for file
4838 ``--tool`` can be used to specify the merge tool used for file
4841 merges. It overrides the HGMERGE environment variable and your
4839 merges. It overrides the HGMERGE environment variable and your
4842 configuration files. See :hg:`help merge-tools` for options.
4840 configuration files. See :hg:`help merge-tools` for options.
4843
4841
4844 If no revision is specified, the working directory's parent is a
4842 If no revision is specified, the working directory's parent is a
4845 head revision, and the current branch contains exactly one other
4843 head revision, and the current branch contains exactly one other
4846 head, the other head is merged with by default. Otherwise, an
4844 head, the other head is merged with by default. Otherwise, an
4847 explicit revision with which to merge must be provided.
4845 explicit revision with which to merge must be provided.
4848
4846
4849 See :hg:`help resolve` for information on handling file conflicts.
4847 See :hg:`help resolve` for information on handling file conflicts.
4850
4848
4851 To undo an uncommitted merge, use :hg:`merge --abort` which
4849 To undo an uncommitted merge, use :hg:`merge --abort` which
4852 will check out a clean copy of the original merge parent, losing
4850 will check out a clean copy of the original merge parent, losing
4853 all changes.
4851 all changes.
4854
4852
4855 Returns 0 on success, 1 if there are unresolved files.
4853 Returns 0 on success, 1 if there are unresolved files.
4856 """
4854 """
4857
4855
4858 opts = pycompat.byteskwargs(opts)
4856 opts = pycompat.byteskwargs(opts)
4859 abort = opts.get(b'abort')
4857 abort = opts.get(b'abort')
4860 if abort and repo.dirstate.p2() == nullid:
4858 if abort and repo.dirstate.p2() == nullid:
4861 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4859 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4862 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4860 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4863 if abort:
4861 if abort:
4864 state = cmdutil.getunfinishedstate(repo)
4862 state = cmdutil.getunfinishedstate(repo)
4865 if state and state._opname != b'merge':
4863 if state and state._opname != b'merge':
4866 raise error.Abort(
4864 raise error.Abort(
4867 _(b'cannot abort merge with %s in progress') % (state._opname),
4865 _(b'cannot abort merge with %s in progress') % (state._opname),
4868 hint=state.hint(),
4866 hint=state.hint(),
4869 )
4867 )
4870 if node:
4868 if node:
4871 raise error.Abort(_(b"cannot specify a node with --abort"))
4869 raise error.Abort(_(b"cannot specify a node with --abort"))
4872 return hg.abortmerge(repo.ui, repo)
4870 return hg.abortmerge(repo.ui, repo)
4873
4871
4874 if opts.get(b'rev') and node:
4872 if opts.get(b'rev') and node:
4875 raise error.Abort(_(b"please specify just one revision"))
4873 raise error.Abort(_(b"please specify just one revision"))
4876 if not node:
4874 if not node:
4877 node = opts.get(b'rev')
4875 node = opts.get(b'rev')
4878
4876
4879 if node:
4877 if node:
4880 ctx = scmutil.revsingle(repo, node)
4878 ctx = scmutil.revsingle(repo, node)
4881 else:
4879 else:
4882 if ui.configbool(b'commands', b'merge.require-rev'):
4880 if ui.configbool(b'commands', b'merge.require-rev'):
4883 raise error.Abort(
4881 raise error.Abort(
4884 _(
4882 _(
4885 b'configuration requires specifying revision to merge '
4883 b'configuration requires specifying revision to merge '
4886 b'with'
4884 b'with'
4887 )
4885 )
4888 )
4886 )
4889 ctx = repo[destutil.destmerge(repo)]
4887 ctx = repo[destutil.destmerge(repo)]
4890
4888
4891 if ctx.node() is None:
4889 if ctx.node() is None:
4892 raise error.Abort(_(b'merging with the working copy has no effect'))
4890 raise error.Abort(_(b'merging with the working copy has no effect'))
4893
4891
4894 if opts.get(b'preview'):
4892 if opts.get(b'preview'):
4895 # find nodes that are ancestors of p2 but not of p1
4893 # find nodes that are ancestors of p2 but not of p1
4896 p1 = repo[b'.'].node()
4894 p1 = repo[b'.'].node()
4897 p2 = ctx.node()
4895 p2 = ctx.node()
4898 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4896 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4899
4897
4900 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4898 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4901 for node in nodes:
4899 for node in nodes:
4902 displayer.show(repo[node])
4900 displayer.show(repo[node])
4903 displayer.close()
4901 displayer.close()
4904 return 0
4902 return 0
4905
4903
4906 # ui.forcemerge is an internal variable, do not document
4904 # ui.forcemerge is an internal variable, do not document
4907 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4905 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4908 with ui.configoverride(overrides, b'merge'):
4906 with ui.configoverride(overrides, b'merge'):
4909 force = opts.get(b'force')
4907 force = opts.get(b'force')
4910 labels = [b'working copy', b'merge rev']
4908 labels = [b'working copy', b'merge rev']
4911 return hg.merge(ctx, force=force, labels=labels)
4909 return hg.merge(ctx, force=force, labels=labels)
4912
4910
4913
4911
4914 statemod.addunfinished(
4912 statemod.addunfinished(
4915 b'merge',
4913 b'merge',
4916 fname=None,
4914 fname=None,
4917 clearable=True,
4915 clearable=True,
4918 allowcommit=True,
4916 allowcommit=True,
4919 cmdmsg=_(b'outstanding uncommitted merge'),
4917 cmdmsg=_(b'outstanding uncommitted merge'),
4920 abortfunc=hg.abortmerge,
4918 abortfunc=hg.abortmerge,
4921 statushint=_(
4919 statushint=_(
4922 b'To continue: hg commit\nTo abort: hg merge --abort'
4920 b'To continue: hg commit\nTo abort: hg merge --abort'
4923 ),
4921 ),
4924 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4922 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4925 )
4923 )
4926
4924
4927
4925
4928 @command(
4926 @command(
4929 b'outgoing|out',
4927 b'outgoing|out',
4930 [
4928 [
4931 (
4929 (
4932 b'f',
4930 b'f',
4933 b'force',
4931 b'force',
4934 None,
4932 None,
4935 _(b'run even when the destination is unrelated'),
4933 _(b'run even when the destination is unrelated'),
4936 ),
4934 ),
4937 (
4935 (
4938 b'r',
4936 b'r',
4939 b'rev',
4937 b'rev',
4940 [],
4938 [],
4941 _(b'a changeset intended to be included in the destination'),
4939 _(b'a changeset intended to be included in the destination'),
4942 _(b'REV'),
4940 _(b'REV'),
4943 ),
4941 ),
4944 (b'n', b'newest-first', None, _(b'show newest record first')),
4942 (b'n', b'newest-first', None, _(b'show newest record first')),
4945 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4943 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4946 (
4944 (
4947 b'b',
4945 b'b',
4948 b'branch',
4946 b'branch',
4949 [],
4947 [],
4950 _(b'a specific branch you would like to push'),
4948 _(b'a specific branch you would like to push'),
4951 _(b'BRANCH'),
4949 _(b'BRANCH'),
4952 ),
4950 ),
4953 ]
4951 ]
4954 + logopts
4952 + logopts
4955 + remoteopts
4953 + remoteopts
4956 + subrepoopts,
4954 + subrepoopts,
4957 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4955 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4958 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4956 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4959 )
4957 )
4960 def outgoing(ui, repo, dest=None, **opts):
4958 def outgoing(ui, repo, dest=None, **opts):
4961 """show changesets not found in the destination
4959 """show changesets not found in the destination
4962
4960
4963 Show changesets not found in the specified destination repository
4961 Show changesets not found in the specified destination repository
4964 or the default push location. These are the changesets that would
4962 or the default push location. These are the changesets that would
4965 be pushed if a push was requested.
4963 be pushed if a push was requested.
4966
4964
4967 See pull for details of valid destination formats.
4965 See pull for details of valid destination formats.
4968
4966
4969 .. container:: verbose
4967 .. container:: verbose
4970
4968
4971 With -B/--bookmarks, the result of bookmark comparison between
4969 With -B/--bookmarks, the result of bookmark comparison between
4972 local and remote repositories is displayed. With -v/--verbose,
4970 local and remote repositories is displayed. With -v/--verbose,
4973 status is also displayed for each bookmark like below::
4971 status is also displayed for each bookmark like below::
4974
4972
4975 BM1 01234567890a added
4973 BM1 01234567890a added
4976 BM2 deleted
4974 BM2 deleted
4977 BM3 234567890abc advanced
4975 BM3 234567890abc advanced
4978 BM4 34567890abcd diverged
4976 BM4 34567890abcd diverged
4979 BM5 4567890abcde changed
4977 BM5 4567890abcde changed
4980
4978
4981 The action taken when pushing depends on the
4979 The action taken when pushing depends on the
4982 status of each bookmark:
4980 status of each bookmark:
4983
4981
4984 :``added``: push with ``-B`` will create it
4982 :``added``: push with ``-B`` will create it
4985 :``deleted``: push with ``-B`` will delete it
4983 :``deleted``: push with ``-B`` will delete it
4986 :``advanced``: push will update it
4984 :``advanced``: push will update it
4987 :``diverged``: push with ``-B`` will update it
4985 :``diverged``: push with ``-B`` will update it
4988 :``changed``: push with ``-B`` will update it
4986 :``changed``: push with ``-B`` will update it
4989
4987
4990 From the point of view of pushing behavior, bookmarks
4988 From the point of view of pushing behavior, bookmarks
4991 existing only in the remote repository are treated as
4989 existing only in the remote repository are treated as
4992 ``deleted``, even if it is in fact added remotely.
4990 ``deleted``, even if it is in fact added remotely.
4993
4991
4994 Returns 0 if there are outgoing changes, 1 otherwise.
4992 Returns 0 if there are outgoing changes, 1 otherwise.
4995 """
4993 """
4996 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4994 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4997 # style URLs, so don't overwrite dest.
4995 # style URLs, so don't overwrite dest.
4998 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4996 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4999 if not path:
4997 if not path:
5000 raise error.Abort(
4998 raise error.Abort(
5001 _(b'default repository not configured!'),
4999 _(b'default repository not configured!'),
5002 hint=_(b"see 'hg help config.paths'"),
5000 hint=_(b"see 'hg help config.paths'"),
5003 )
5001 )
5004
5002
5005 opts = pycompat.byteskwargs(opts)
5003 opts = pycompat.byteskwargs(opts)
5006 if opts.get(b'graph'):
5004 if opts.get(b'graph'):
5007 logcmdutil.checkunsupportedgraphflags([], opts)
5005 logcmdutil.checkunsupportedgraphflags([], opts)
5008 o, other = hg._outgoing(ui, repo, dest, opts)
5006 o, other = hg._outgoing(ui, repo, dest, opts)
5009 if not o:
5007 if not o:
5010 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5008 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5011 return
5009 return
5012
5010
5013 revdag = logcmdutil.graphrevs(repo, o, opts)
5011 revdag = logcmdutil.graphrevs(repo, o, opts)
5014 ui.pager(b'outgoing')
5012 ui.pager(b'outgoing')
5015 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5013 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5016 logcmdutil.displaygraph(
5014 logcmdutil.displaygraph(
5017 ui, repo, revdag, displayer, graphmod.asciiedges
5015 ui, repo, revdag, displayer, graphmod.asciiedges
5018 )
5016 )
5019 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5017 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5020 return 0
5018 return 0
5021
5019
5022 if opts.get(b'bookmarks'):
5020 if opts.get(b'bookmarks'):
5023 dest = path.pushloc or path.loc
5021 dest = path.pushloc or path.loc
5024 other = hg.peer(repo, opts, dest)
5022 other = hg.peer(repo, opts, dest)
5025 if b'bookmarks' not in other.listkeys(b'namespaces'):
5023 if b'bookmarks' not in other.listkeys(b'namespaces'):
5026 ui.warn(_(b"remote doesn't support bookmarks\n"))
5024 ui.warn(_(b"remote doesn't support bookmarks\n"))
5027 return 0
5025 return 0
5028 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5026 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5029 ui.pager(b'outgoing')
5027 ui.pager(b'outgoing')
5030 return bookmarks.outgoing(ui, repo, other)
5028 return bookmarks.outgoing(ui, repo, other)
5031
5029
5032 repo._subtoppath = path.pushloc or path.loc
5030 repo._subtoppath = path.pushloc or path.loc
5033 try:
5031 try:
5034 return hg.outgoing(ui, repo, dest, opts)
5032 return hg.outgoing(ui, repo, dest, opts)
5035 finally:
5033 finally:
5036 del repo._subtoppath
5034 del repo._subtoppath
5037
5035
5038
5036
5039 @command(
5037 @command(
5040 b'parents',
5038 b'parents',
5041 [
5039 [
5042 (
5040 (
5043 b'r',
5041 b'r',
5044 b'rev',
5042 b'rev',
5045 b'',
5043 b'',
5046 _(b'show parents of the specified revision'),
5044 _(b'show parents of the specified revision'),
5047 _(b'REV'),
5045 _(b'REV'),
5048 ),
5046 ),
5049 ]
5047 ]
5050 + templateopts,
5048 + templateopts,
5051 _(b'[-r REV] [FILE]'),
5049 _(b'[-r REV] [FILE]'),
5052 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5050 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5053 inferrepo=True,
5051 inferrepo=True,
5054 )
5052 )
5055 def parents(ui, repo, file_=None, **opts):
5053 def parents(ui, repo, file_=None, **opts):
5056 """show the parents of the working directory or revision (DEPRECATED)
5054 """show the parents of the working directory or revision (DEPRECATED)
5057
5055
5058 Print the working directory's parent revisions. If a revision is
5056 Print the working directory's parent revisions. If a revision is
5059 given via -r/--rev, the parent of that revision will be printed.
5057 given via -r/--rev, the parent of that revision will be printed.
5060 If a file argument is given, the revision in which the file was
5058 If a file argument is given, the revision in which the file was
5061 last changed (before the working directory revision or the
5059 last changed (before the working directory revision or the
5062 argument to --rev if given) is printed.
5060 argument to --rev if given) is printed.
5063
5061
5064 This command is equivalent to::
5062 This command is equivalent to::
5065
5063
5066 hg log -r "p1()+p2()" or
5064 hg log -r "p1()+p2()" or
5067 hg log -r "p1(REV)+p2(REV)" or
5065 hg log -r "p1(REV)+p2(REV)" or
5068 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5066 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5069 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5067 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5070
5068
5071 See :hg:`summary` and :hg:`help revsets` for related information.
5069 See :hg:`summary` and :hg:`help revsets` for related information.
5072
5070
5073 Returns 0 on success.
5071 Returns 0 on success.
5074 """
5072 """
5075
5073
5076 opts = pycompat.byteskwargs(opts)
5074 opts = pycompat.byteskwargs(opts)
5077 rev = opts.get(b'rev')
5075 rev = opts.get(b'rev')
5078 if rev:
5076 if rev:
5079 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5077 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5080 ctx = scmutil.revsingle(repo, rev, None)
5078 ctx = scmutil.revsingle(repo, rev, None)
5081
5079
5082 if file_:
5080 if file_:
5083 m = scmutil.match(ctx, (file_,), opts)
5081 m = scmutil.match(ctx, (file_,), opts)
5084 if m.anypats() or len(m.files()) != 1:
5082 if m.anypats() or len(m.files()) != 1:
5085 raise error.Abort(_(b'can only specify an explicit filename'))
5083 raise error.Abort(_(b'can only specify an explicit filename'))
5086 file_ = m.files()[0]
5084 file_ = m.files()[0]
5087 filenodes = []
5085 filenodes = []
5088 for cp in ctx.parents():
5086 for cp in ctx.parents():
5089 if not cp:
5087 if not cp:
5090 continue
5088 continue
5091 try:
5089 try:
5092 filenodes.append(cp.filenode(file_))
5090 filenodes.append(cp.filenode(file_))
5093 except error.LookupError:
5091 except error.LookupError:
5094 pass
5092 pass
5095 if not filenodes:
5093 if not filenodes:
5096 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5094 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5097 p = []
5095 p = []
5098 for fn in filenodes:
5096 for fn in filenodes:
5099 fctx = repo.filectx(file_, fileid=fn)
5097 fctx = repo.filectx(file_, fileid=fn)
5100 p.append(fctx.node())
5098 p.append(fctx.node())
5101 else:
5099 else:
5102 p = [cp.node() for cp in ctx.parents()]
5100 p = [cp.node() for cp in ctx.parents()]
5103
5101
5104 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5102 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5105 for n in p:
5103 for n in p:
5106 if n != nullid:
5104 if n != nullid:
5107 displayer.show(repo[n])
5105 displayer.show(repo[n])
5108 displayer.close()
5106 displayer.close()
5109
5107
5110
5108
5111 @command(
5109 @command(
5112 b'paths',
5110 b'paths',
5113 formatteropts,
5111 formatteropts,
5114 _(b'[NAME]'),
5112 _(b'[NAME]'),
5115 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5113 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5116 optionalrepo=True,
5114 optionalrepo=True,
5117 intents={INTENT_READONLY},
5115 intents={INTENT_READONLY},
5118 )
5116 )
5119 def paths(ui, repo, search=None, **opts):
5117 def paths(ui, repo, search=None, **opts):
5120 """show aliases for remote repositories
5118 """show aliases for remote repositories
5121
5119
5122 Show definition of symbolic path name NAME. If no name is given,
5120 Show definition of symbolic path name NAME. If no name is given,
5123 show definition of all available names.
5121 show definition of all available names.
5124
5122
5125 Option -q/--quiet suppresses all output when searching for NAME
5123 Option -q/--quiet suppresses all output when searching for NAME
5126 and shows only the path names when listing all definitions.
5124 and shows only the path names when listing all definitions.
5127
5125
5128 Path names are defined in the [paths] section of your
5126 Path names are defined in the [paths] section of your
5129 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5127 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5130 repository, ``.hg/hgrc`` is used, too.
5128 repository, ``.hg/hgrc`` is used, too.
5131
5129
5132 The path names ``default`` and ``default-push`` have a special
5130 The path names ``default`` and ``default-push`` have a special
5133 meaning. When performing a push or pull operation, they are used
5131 meaning. When performing a push or pull operation, they are used
5134 as fallbacks if no location is specified on the command-line.
5132 as fallbacks if no location is specified on the command-line.
5135 When ``default-push`` is set, it will be used for push and
5133 When ``default-push`` is set, it will be used for push and
5136 ``default`` will be used for pull; otherwise ``default`` is used
5134 ``default`` will be used for pull; otherwise ``default`` is used
5137 as the fallback for both. When cloning a repository, the clone
5135 as the fallback for both. When cloning a repository, the clone
5138 source is written as ``default`` in ``.hg/hgrc``.
5136 source is written as ``default`` in ``.hg/hgrc``.
5139
5137
5140 .. note::
5138 .. note::
5141
5139
5142 ``default`` and ``default-push`` apply to all inbound (e.g.
5140 ``default`` and ``default-push`` apply to all inbound (e.g.
5143 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5141 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5144 and :hg:`bundle`) operations.
5142 and :hg:`bundle`) operations.
5145
5143
5146 See :hg:`help urls` for more information.
5144 See :hg:`help urls` for more information.
5147
5145
5148 .. container:: verbose
5146 .. container:: verbose
5149
5147
5150 Template:
5148 Template:
5151
5149
5152 The following keywords are supported. See also :hg:`help templates`.
5150 The following keywords are supported. See also :hg:`help templates`.
5153
5151
5154 :name: String. Symbolic name of the path alias.
5152 :name: String. Symbolic name of the path alias.
5155 :pushurl: String. URL for push operations.
5153 :pushurl: String. URL for push operations.
5156 :url: String. URL or directory path for the other operations.
5154 :url: String. URL or directory path for the other operations.
5157
5155
5158 Returns 0 on success.
5156 Returns 0 on success.
5159 """
5157 """
5160
5158
5161 opts = pycompat.byteskwargs(opts)
5159 opts = pycompat.byteskwargs(opts)
5162 ui.pager(b'paths')
5160 ui.pager(b'paths')
5163 if search:
5161 if search:
5164 pathitems = [
5162 pathitems = [
5165 (name, path)
5163 (name, path)
5166 for name, path in pycompat.iteritems(ui.paths)
5164 for name, path in pycompat.iteritems(ui.paths)
5167 if name == search
5165 if name == search
5168 ]
5166 ]
5169 else:
5167 else:
5170 pathitems = sorted(pycompat.iteritems(ui.paths))
5168 pathitems = sorted(pycompat.iteritems(ui.paths))
5171
5169
5172 fm = ui.formatter(b'paths', opts)
5170 fm = ui.formatter(b'paths', opts)
5173 if fm.isplain():
5171 if fm.isplain():
5174 hidepassword = util.hidepassword
5172 hidepassword = util.hidepassword
5175 else:
5173 else:
5176 hidepassword = bytes
5174 hidepassword = bytes
5177 if ui.quiet:
5175 if ui.quiet:
5178 namefmt = b'%s\n'
5176 namefmt = b'%s\n'
5179 else:
5177 else:
5180 namefmt = b'%s = '
5178 namefmt = b'%s = '
5181 showsubopts = not search and not ui.quiet
5179 showsubopts = not search and not ui.quiet
5182
5180
5183 for name, path in pathitems:
5181 for name, path in pathitems:
5184 fm.startitem()
5182 fm.startitem()
5185 fm.condwrite(not search, b'name', namefmt, name)
5183 fm.condwrite(not search, b'name', namefmt, name)
5186 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5184 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5187 for subopt, value in sorted(path.suboptions.items()):
5185 for subopt, value in sorted(path.suboptions.items()):
5188 assert subopt not in (b'name', b'url')
5186 assert subopt not in (b'name', b'url')
5189 if showsubopts:
5187 if showsubopts:
5190 fm.plain(b'%s:%s = ' % (name, subopt))
5188 fm.plain(b'%s:%s = ' % (name, subopt))
5191 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5189 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5192
5190
5193 fm.end()
5191 fm.end()
5194
5192
5195 if search and not pathitems:
5193 if search and not pathitems:
5196 if not ui.quiet:
5194 if not ui.quiet:
5197 ui.warn(_(b"not found!\n"))
5195 ui.warn(_(b"not found!\n"))
5198 return 1
5196 return 1
5199 else:
5197 else:
5200 return 0
5198 return 0
5201
5199
5202
5200
5203 @command(
5201 @command(
5204 b'phase',
5202 b'phase',
5205 [
5203 [
5206 (b'p', b'public', False, _(b'set changeset phase to public')),
5204 (b'p', b'public', False, _(b'set changeset phase to public')),
5207 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5205 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5208 (b's', b'secret', False, _(b'set changeset phase to secret')),
5206 (b's', b'secret', False, _(b'set changeset phase to secret')),
5209 (b'f', b'force', False, _(b'allow to move boundary backward')),
5207 (b'f', b'force', False, _(b'allow to move boundary backward')),
5210 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5208 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5211 ],
5209 ],
5212 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5210 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5213 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5211 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5214 )
5212 )
5215 def phase(ui, repo, *revs, **opts):
5213 def phase(ui, repo, *revs, **opts):
5216 """set or show the current phase name
5214 """set or show the current phase name
5217
5215
5218 With no argument, show the phase name of the current revision(s).
5216 With no argument, show the phase name of the current revision(s).
5219
5217
5220 With one of -p/--public, -d/--draft or -s/--secret, change the
5218 With one of -p/--public, -d/--draft or -s/--secret, change the
5221 phase value of the specified revisions.
5219 phase value of the specified revisions.
5222
5220
5223 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5221 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5224 lower phase to a higher phase. Phases are ordered as follows::
5222 lower phase to a higher phase. Phases are ordered as follows::
5225
5223
5226 public < draft < secret
5224 public < draft < secret
5227
5225
5228 Returns 0 on success, 1 if some phases could not be changed.
5226 Returns 0 on success, 1 if some phases could not be changed.
5229
5227
5230 (For more information about the phases concept, see :hg:`help phases`.)
5228 (For more information about the phases concept, see :hg:`help phases`.)
5231 """
5229 """
5232 opts = pycompat.byteskwargs(opts)
5230 opts = pycompat.byteskwargs(opts)
5233 # search for a unique phase argument
5231 # search for a unique phase argument
5234 targetphase = None
5232 targetphase = None
5235 for idx, name in enumerate(phases.cmdphasenames):
5233 for idx, name in enumerate(phases.cmdphasenames):
5236 if opts[name]:
5234 if opts[name]:
5237 if targetphase is not None:
5235 if targetphase is not None:
5238 raise error.Abort(_(b'only one phase can be specified'))
5236 raise error.Abort(_(b'only one phase can be specified'))
5239 targetphase = idx
5237 targetphase = idx
5240
5238
5241 # look for specified revision
5239 # look for specified revision
5242 revs = list(revs)
5240 revs = list(revs)
5243 revs.extend(opts[b'rev'])
5241 revs.extend(opts[b'rev'])
5244 if not revs:
5242 if not revs:
5245 # display both parents as the second parent phase can influence
5243 # display both parents as the second parent phase can influence
5246 # the phase of a merge commit
5244 # the phase of a merge commit
5247 revs = [c.rev() for c in repo[None].parents()]
5245 revs = [c.rev() for c in repo[None].parents()]
5248
5246
5249 revs = scmutil.revrange(repo, revs)
5247 revs = scmutil.revrange(repo, revs)
5250
5248
5251 ret = 0
5249 ret = 0
5252 if targetphase is None:
5250 if targetphase is None:
5253 # display
5251 # display
5254 for r in revs:
5252 for r in revs:
5255 ctx = repo[r]
5253 ctx = repo[r]
5256 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5254 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5257 else:
5255 else:
5258 with repo.lock(), repo.transaction(b"phase") as tr:
5256 with repo.lock(), repo.transaction(b"phase") as tr:
5259 # set phase
5257 # set phase
5260 if not revs:
5258 if not revs:
5261 raise error.Abort(_(b'empty revision set'))
5259 raise error.Abort(_(b'empty revision set'))
5262 nodes = [repo[r].node() for r in revs]
5260 nodes = [repo[r].node() for r in revs]
5263 # moving revision from public to draft may hide them
5261 # moving revision from public to draft may hide them
5264 # We have to check result on an unfiltered repository
5262 # We have to check result on an unfiltered repository
5265 unfi = repo.unfiltered()
5263 unfi = repo.unfiltered()
5266 getphase = unfi._phasecache.phase
5264 getphase = unfi._phasecache.phase
5267 olddata = [getphase(unfi, r) for r in unfi]
5265 olddata = [getphase(unfi, r) for r in unfi]
5268 phases.advanceboundary(repo, tr, targetphase, nodes)
5266 phases.advanceboundary(repo, tr, targetphase, nodes)
5269 if opts[b'force']:
5267 if opts[b'force']:
5270 phases.retractboundary(repo, tr, targetphase, nodes)
5268 phases.retractboundary(repo, tr, targetphase, nodes)
5271 getphase = unfi._phasecache.phase
5269 getphase = unfi._phasecache.phase
5272 newdata = [getphase(unfi, r) for r in unfi]
5270 newdata = [getphase(unfi, r) for r in unfi]
5273 changes = sum(newdata[r] != olddata[r] for r in unfi)
5271 changes = sum(newdata[r] != olddata[r] for r in unfi)
5274 cl = unfi.changelog
5272 cl = unfi.changelog
5275 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5273 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5276 if rejected:
5274 if rejected:
5277 ui.warn(
5275 ui.warn(
5278 _(
5276 _(
5279 b'cannot move %i changesets to a higher '
5277 b'cannot move %i changesets to a higher '
5280 b'phase, use --force\n'
5278 b'phase, use --force\n'
5281 )
5279 )
5282 % len(rejected)
5280 % len(rejected)
5283 )
5281 )
5284 ret = 1
5282 ret = 1
5285 if changes:
5283 if changes:
5286 msg = _(b'phase changed for %i changesets\n') % changes
5284 msg = _(b'phase changed for %i changesets\n') % changes
5287 if ret:
5285 if ret:
5288 ui.status(msg)
5286 ui.status(msg)
5289 else:
5287 else:
5290 ui.note(msg)
5288 ui.note(msg)
5291 else:
5289 else:
5292 ui.warn(_(b'no phases changed\n'))
5290 ui.warn(_(b'no phases changed\n'))
5293 return ret
5291 return ret
5294
5292
5295
5293
5296 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5294 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5297 """Run after a changegroup has been added via pull/unbundle
5295 """Run after a changegroup has been added via pull/unbundle
5298
5296
5299 This takes arguments below:
5297 This takes arguments below:
5300
5298
5301 :modheads: change of heads by pull/unbundle
5299 :modheads: change of heads by pull/unbundle
5302 :optupdate: updating working directory is needed or not
5300 :optupdate: updating working directory is needed or not
5303 :checkout: update destination revision (or None to default destination)
5301 :checkout: update destination revision (or None to default destination)
5304 :brev: a name, which might be a bookmark to be activated after updating
5302 :brev: a name, which might be a bookmark to be activated after updating
5305 """
5303 """
5306 if modheads == 0:
5304 if modheads == 0:
5307 return
5305 return
5308 if optupdate:
5306 if optupdate:
5309 try:
5307 try:
5310 return hg.updatetotally(ui, repo, checkout, brev)
5308 return hg.updatetotally(ui, repo, checkout, brev)
5311 except error.UpdateAbort as inst:
5309 except error.UpdateAbort as inst:
5312 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5310 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5313 hint = inst.hint
5311 hint = inst.hint
5314 raise error.UpdateAbort(msg, hint=hint)
5312 raise error.UpdateAbort(msg, hint=hint)
5315 if modheads is not None and modheads > 1:
5313 if modheads is not None and modheads > 1:
5316 currentbranchheads = len(repo.branchheads())
5314 currentbranchheads = len(repo.branchheads())
5317 if currentbranchheads == modheads:
5315 if currentbranchheads == modheads:
5318 ui.status(
5316 ui.status(
5319 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5317 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5320 )
5318 )
5321 elif currentbranchheads > 1:
5319 elif currentbranchheads > 1:
5322 ui.status(
5320 ui.status(
5323 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5321 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5324 )
5322 )
5325 else:
5323 else:
5326 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5324 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5327 elif not ui.configbool(b'commands', b'update.requiredest'):
5325 elif not ui.configbool(b'commands', b'update.requiredest'):
5328 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5326 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5329
5327
5330
5328
5331 @command(
5329 @command(
5332 b'pull',
5330 b'pull',
5333 [
5331 [
5334 (
5332 (
5335 b'u',
5333 b'u',
5336 b'update',
5334 b'update',
5337 None,
5335 None,
5338 _(b'update to new branch head if new descendants were pulled'),
5336 _(b'update to new branch head if new descendants were pulled'),
5339 ),
5337 ),
5340 (
5338 (
5341 b'f',
5339 b'f',
5342 b'force',
5340 b'force',
5343 None,
5341 None,
5344 _(b'run even when remote repository is unrelated'),
5342 _(b'run even when remote repository is unrelated'),
5345 ),
5343 ),
5346 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5344 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5347 (
5345 (
5348 b'r',
5346 b'r',
5349 b'rev',
5347 b'rev',
5350 [],
5348 [],
5351 _(b'a remote changeset intended to be added'),
5349 _(b'a remote changeset intended to be added'),
5352 _(b'REV'),
5350 _(b'REV'),
5353 ),
5351 ),
5354 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5352 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5355 (
5353 (
5356 b'b',
5354 b'b',
5357 b'branch',
5355 b'branch',
5358 [],
5356 [],
5359 _(b'a specific branch you would like to pull'),
5357 _(b'a specific branch you would like to pull'),
5360 _(b'BRANCH'),
5358 _(b'BRANCH'),
5361 ),
5359 ),
5362 ]
5360 ]
5363 + remoteopts,
5361 + remoteopts,
5364 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5362 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5365 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5363 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5366 helpbasic=True,
5364 helpbasic=True,
5367 )
5365 )
5368 def pull(ui, repo, source=b"default", **opts):
5366 def pull(ui, repo, source=b"default", **opts):
5369 """pull changes from the specified source
5367 """pull changes from the specified source
5370
5368
5371 Pull changes from a remote repository to a local one.
5369 Pull changes from a remote repository to a local one.
5372
5370
5373 This finds all changes from the repository at the specified path
5371 This finds all changes from the repository at the specified path
5374 or URL and adds them to a local repository (the current one unless
5372 or URL and adds them to a local repository (the current one unless
5375 -R is specified). By default, this does not update the copy of the
5373 -R is specified). By default, this does not update the copy of the
5376 project in the working directory.
5374 project in the working directory.
5377
5375
5378 When cloning from servers that support it, Mercurial may fetch
5376 When cloning from servers that support it, Mercurial may fetch
5379 pre-generated data. When this is done, hooks operating on incoming
5377 pre-generated data. When this is done, hooks operating on incoming
5380 changesets and changegroups may fire more than once, once for each
5378 changesets and changegroups may fire more than once, once for each
5381 pre-generated bundle and as well as for any additional remaining
5379 pre-generated bundle and as well as for any additional remaining
5382 data. See :hg:`help -e clonebundles` for more.
5380 data. See :hg:`help -e clonebundles` for more.
5383
5381
5384 Use :hg:`incoming` if you want to see what would have been added
5382 Use :hg:`incoming` if you want to see what would have been added
5385 by a pull at the time you issued this command. If you then decide
5383 by a pull at the time you issued this command. If you then decide
5386 to add those changes to the repository, you should use :hg:`pull
5384 to add those changes to the repository, you should use :hg:`pull
5387 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5385 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5388
5386
5389 If SOURCE is omitted, the 'default' path will be used.
5387 If SOURCE is omitted, the 'default' path will be used.
5390 See :hg:`help urls` for more information.
5388 See :hg:`help urls` for more information.
5391
5389
5392 Specifying bookmark as ``.`` is equivalent to specifying the active
5390 Specifying bookmark as ``.`` is equivalent to specifying the active
5393 bookmark's name.
5391 bookmark's name.
5394
5392
5395 Returns 0 on success, 1 if an update had unresolved files.
5393 Returns 0 on success, 1 if an update had unresolved files.
5396 """
5394 """
5397
5395
5398 opts = pycompat.byteskwargs(opts)
5396 opts = pycompat.byteskwargs(opts)
5399 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5397 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5400 b'update'
5398 b'update'
5401 ):
5399 ):
5402 msg = _(b'update destination required by configuration')
5400 msg = _(b'update destination required by configuration')
5403 hint = _(b'use hg pull followed by hg update DEST')
5401 hint = _(b'use hg pull followed by hg update DEST')
5404 raise error.Abort(msg, hint=hint)
5402 raise error.Abort(msg, hint=hint)
5405
5403
5406 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5404 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5407 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5405 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5408 other = hg.peer(repo, opts, source)
5406 other = hg.peer(repo, opts, source)
5409 try:
5407 try:
5410 revs, checkout = hg.addbranchrevs(
5408 revs, checkout = hg.addbranchrevs(
5411 repo, other, branches, opts.get(b'rev')
5409 repo, other, branches, opts.get(b'rev')
5412 )
5410 )
5413
5411
5414 pullopargs = {}
5412 pullopargs = {}
5415
5413
5416 nodes = None
5414 nodes = None
5417 if opts.get(b'bookmark') or revs:
5415 if opts.get(b'bookmark') or revs:
5418 # The list of bookmark used here is the same used to actually update
5416 # The list of bookmark used here is the same used to actually update
5419 # the bookmark names, to avoid the race from issue 4689 and we do
5417 # the bookmark names, to avoid the race from issue 4689 and we do
5420 # all lookup and bookmark queries in one go so they see the same
5418 # all lookup and bookmark queries in one go so they see the same
5421 # version of the server state (issue 4700).
5419 # version of the server state (issue 4700).
5422 nodes = []
5420 nodes = []
5423 fnodes = []
5421 fnodes = []
5424 revs = revs or []
5422 revs = revs or []
5425 if revs and not other.capable(b'lookup'):
5423 if revs and not other.capable(b'lookup'):
5426 err = _(
5424 err = _(
5427 b"other repository doesn't support revision lookup, "
5425 b"other repository doesn't support revision lookup, "
5428 b"so a rev cannot be specified."
5426 b"so a rev cannot be specified."
5429 )
5427 )
5430 raise error.Abort(err)
5428 raise error.Abort(err)
5431 with other.commandexecutor() as e:
5429 with other.commandexecutor() as e:
5432 fremotebookmarks = e.callcommand(
5430 fremotebookmarks = e.callcommand(
5433 b'listkeys', {b'namespace': b'bookmarks'}
5431 b'listkeys', {b'namespace': b'bookmarks'}
5434 )
5432 )
5435 for r in revs:
5433 for r in revs:
5436 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5434 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5437 remotebookmarks = fremotebookmarks.result()
5435 remotebookmarks = fremotebookmarks.result()
5438 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5436 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5439 pullopargs[b'remotebookmarks'] = remotebookmarks
5437 pullopargs[b'remotebookmarks'] = remotebookmarks
5440 for b in opts.get(b'bookmark', []):
5438 for b in opts.get(b'bookmark', []):
5441 b = repo._bookmarks.expandname(b)
5439 b = repo._bookmarks.expandname(b)
5442 if b not in remotebookmarks:
5440 if b not in remotebookmarks:
5443 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5441 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5444 nodes.append(remotebookmarks[b])
5442 nodes.append(remotebookmarks[b])
5445 for i, rev in enumerate(revs):
5443 for i, rev in enumerate(revs):
5446 node = fnodes[i].result()
5444 node = fnodes[i].result()
5447 nodes.append(node)
5445 nodes.append(node)
5448 if rev == checkout:
5446 if rev == checkout:
5449 checkout = node
5447 checkout = node
5450
5448
5451 wlock = util.nullcontextmanager()
5449 wlock = util.nullcontextmanager()
5452 if opts.get(b'update'):
5450 if opts.get(b'update'):
5453 wlock = repo.wlock()
5451 wlock = repo.wlock()
5454 with wlock:
5452 with wlock:
5455 pullopargs.update(opts.get(b'opargs', {}))
5453 pullopargs.update(opts.get(b'opargs', {}))
5456 modheads = exchange.pull(
5454 modheads = exchange.pull(
5457 repo,
5455 repo,
5458 other,
5456 other,
5459 heads=nodes,
5457 heads=nodes,
5460 force=opts.get(b'force'),
5458 force=opts.get(b'force'),
5461 bookmarks=opts.get(b'bookmark', ()),
5459 bookmarks=opts.get(b'bookmark', ()),
5462 opargs=pullopargs,
5460 opargs=pullopargs,
5463 confirm=opts.get(b'confirm'),
5461 confirm=opts.get(b'confirm'),
5464 ).cgresult
5462 ).cgresult
5465
5463
5466 # brev is a name, which might be a bookmark to be activated at
5464 # brev is a name, which might be a bookmark to be activated at
5467 # the end of the update. In other words, it is an explicit
5465 # the end of the update. In other words, it is an explicit
5468 # destination of the update
5466 # destination of the update
5469 brev = None
5467 brev = None
5470
5468
5471 if checkout:
5469 if checkout:
5472 checkout = repo.unfiltered().changelog.rev(checkout)
5470 checkout = repo.unfiltered().changelog.rev(checkout)
5473
5471
5474 # order below depends on implementation of
5472 # order below depends on implementation of
5475 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5473 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5476 # because 'checkout' is determined without it.
5474 # because 'checkout' is determined without it.
5477 if opts.get(b'rev'):
5475 if opts.get(b'rev'):
5478 brev = opts[b'rev'][0]
5476 brev = opts[b'rev'][0]
5479 elif opts.get(b'branch'):
5477 elif opts.get(b'branch'):
5480 brev = opts[b'branch'][0]
5478 brev = opts[b'branch'][0]
5481 else:
5479 else:
5482 brev = branches[0]
5480 brev = branches[0]
5483 repo._subtoppath = source
5481 repo._subtoppath = source
5484 try:
5482 try:
5485 ret = postincoming(
5483 ret = postincoming(
5486 ui, repo, modheads, opts.get(b'update'), checkout, brev
5484 ui, repo, modheads, opts.get(b'update'), checkout, brev
5487 )
5485 )
5488 except error.FilteredRepoLookupError as exc:
5486 except error.FilteredRepoLookupError as exc:
5489 msg = _(b'cannot update to target: %s') % exc.args[0]
5487 msg = _(b'cannot update to target: %s') % exc.args[0]
5490 exc.args = (msg,) + exc.args[1:]
5488 exc.args = (msg,) + exc.args[1:]
5491 raise
5489 raise
5492 finally:
5490 finally:
5493 del repo._subtoppath
5491 del repo._subtoppath
5494
5492
5495 finally:
5493 finally:
5496 other.close()
5494 other.close()
5497 return ret
5495 return ret
5498
5496
5499
5497
5500 @command(
5498 @command(
5501 b'push',
5499 b'push',
5502 [
5500 [
5503 (b'f', b'force', None, _(b'force push')),
5501 (b'f', b'force', None, _(b'force push')),
5504 (
5502 (
5505 b'r',
5503 b'r',
5506 b'rev',
5504 b'rev',
5507 [],
5505 [],
5508 _(b'a changeset intended to be included in the destination'),
5506 _(b'a changeset intended to be included in the destination'),
5509 _(b'REV'),
5507 _(b'REV'),
5510 ),
5508 ),
5511 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5509 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5512 (
5510 (
5513 b'b',
5511 b'b',
5514 b'branch',
5512 b'branch',
5515 [],
5513 [],
5516 _(b'a specific branch you would like to push'),
5514 _(b'a specific branch you would like to push'),
5517 _(b'BRANCH'),
5515 _(b'BRANCH'),
5518 ),
5516 ),
5519 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5517 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5520 (
5518 (
5521 b'',
5519 b'',
5522 b'pushvars',
5520 b'pushvars',
5523 [],
5521 [],
5524 _(b'variables that can be sent to server (ADVANCED)'),
5522 _(b'variables that can be sent to server (ADVANCED)'),
5525 ),
5523 ),
5526 (
5524 (
5527 b'',
5525 b'',
5528 b'publish',
5526 b'publish',
5529 False,
5527 False,
5530 _(b'push the changeset as public (EXPERIMENTAL)'),
5528 _(b'push the changeset as public (EXPERIMENTAL)'),
5531 ),
5529 ),
5532 ]
5530 ]
5533 + remoteopts,
5531 + remoteopts,
5534 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5532 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5535 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5533 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5536 helpbasic=True,
5534 helpbasic=True,
5537 )
5535 )
5538 def push(ui, repo, dest=None, **opts):
5536 def push(ui, repo, dest=None, **opts):
5539 """push changes to the specified destination
5537 """push changes to the specified destination
5540
5538
5541 Push changesets from the local repository to the specified
5539 Push changesets from the local repository to the specified
5542 destination.
5540 destination.
5543
5541
5544 This operation is symmetrical to pull: it is identical to a pull
5542 This operation is symmetrical to pull: it is identical to a pull
5545 in the destination repository from the current one.
5543 in the destination repository from the current one.
5546
5544
5547 By default, push will not allow creation of new heads at the
5545 By default, push will not allow creation of new heads at the
5548 destination, since multiple heads would make it unclear which head
5546 destination, since multiple heads would make it unclear which head
5549 to use. In this situation, it is recommended to pull and merge
5547 to use. In this situation, it is recommended to pull and merge
5550 before pushing.
5548 before pushing.
5551
5549
5552 Use --new-branch if you want to allow push to create a new named
5550 Use --new-branch if you want to allow push to create a new named
5553 branch that is not present at the destination. This allows you to
5551 branch that is not present at the destination. This allows you to
5554 only create a new branch without forcing other changes.
5552 only create a new branch without forcing other changes.
5555
5553
5556 .. note::
5554 .. note::
5557
5555
5558 Extra care should be taken with the -f/--force option,
5556 Extra care should be taken with the -f/--force option,
5559 which will push all new heads on all branches, an action which will
5557 which will push all new heads on all branches, an action which will
5560 almost always cause confusion for collaborators.
5558 almost always cause confusion for collaborators.
5561
5559
5562 If -r/--rev is used, the specified revision and all its ancestors
5560 If -r/--rev is used, the specified revision and all its ancestors
5563 will be pushed to the remote repository.
5561 will be pushed to the remote repository.
5564
5562
5565 If -B/--bookmark is used, the specified bookmarked revision, its
5563 If -B/--bookmark is used, the specified bookmarked revision, its
5566 ancestors, and the bookmark will be pushed to the remote
5564 ancestors, and the bookmark will be pushed to the remote
5567 repository. Specifying ``.`` is equivalent to specifying the active
5565 repository. Specifying ``.`` is equivalent to specifying the active
5568 bookmark's name.
5566 bookmark's name.
5569
5567
5570 Please see :hg:`help urls` for important details about ``ssh://``
5568 Please see :hg:`help urls` for important details about ``ssh://``
5571 URLs. If DESTINATION is omitted, a default path will be used.
5569 URLs. If DESTINATION is omitted, a default path will be used.
5572
5570
5573 .. container:: verbose
5571 .. container:: verbose
5574
5572
5575 The --pushvars option sends strings to the server that become
5573 The --pushvars option sends strings to the server that become
5576 environment variables prepended with ``HG_USERVAR_``. For example,
5574 environment variables prepended with ``HG_USERVAR_``. For example,
5577 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5575 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5578 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5576 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5579
5577
5580 pushvars can provide for user-overridable hooks as well as set debug
5578 pushvars can provide for user-overridable hooks as well as set debug
5581 levels. One example is having a hook that blocks commits containing
5579 levels. One example is having a hook that blocks commits containing
5582 conflict markers, but enables the user to override the hook if the file
5580 conflict markers, but enables the user to override the hook if the file
5583 is using conflict markers for testing purposes or the file format has
5581 is using conflict markers for testing purposes or the file format has
5584 strings that look like conflict markers.
5582 strings that look like conflict markers.
5585
5583
5586 By default, servers will ignore `--pushvars`. To enable it add the
5584 By default, servers will ignore `--pushvars`. To enable it add the
5587 following to your configuration file::
5585 following to your configuration file::
5588
5586
5589 [push]
5587 [push]
5590 pushvars.server = true
5588 pushvars.server = true
5591
5589
5592 Returns 0 if push was successful, 1 if nothing to push.
5590 Returns 0 if push was successful, 1 if nothing to push.
5593 """
5591 """
5594
5592
5595 opts = pycompat.byteskwargs(opts)
5593 opts = pycompat.byteskwargs(opts)
5596 if opts.get(b'bookmark'):
5594 if opts.get(b'bookmark'):
5597 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5595 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5598 for b in opts[b'bookmark']:
5596 for b in opts[b'bookmark']:
5599 # translate -B options to -r so changesets get pushed
5597 # translate -B options to -r so changesets get pushed
5600 b = repo._bookmarks.expandname(b)
5598 b = repo._bookmarks.expandname(b)
5601 if b in repo._bookmarks:
5599 if b in repo._bookmarks:
5602 opts.setdefault(b'rev', []).append(b)
5600 opts.setdefault(b'rev', []).append(b)
5603 else:
5601 else:
5604 # if we try to push a deleted bookmark, translate it to null
5602 # if we try to push a deleted bookmark, translate it to null
5605 # this lets simultaneous -r, -b options continue working
5603 # this lets simultaneous -r, -b options continue working
5606 opts.setdefault(b'rev', []).append(b"null")
5604 opts.setdefault(b'rev', []).append(b"null")
5607
5605
5608 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5606 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5609 if not path:
5607 if not path:
5610 raise error.Abort(
5608 raise error.Abort(
5611 _(b'default repository not configured!'),
5609 _(b'default repository not configured!'),
5612 hint=_(b"see 'hg help config.paths'"),
5610 hint=_(b"see 'hg help config.paths'"),
5613 )
5611 )
5614 dest = path.pushloc or path.loc
5612 dest = path.pushloc or path.loc
5615 branches = (path.branch, opts.get(b'branch') or [])
5613 branches = (path.branch, opts.get(b'branch') or [])
5616 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5614 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5617 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5615 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5618 other = hg.peer(repo, opts, dest)
5616 other = hg.peer(repo, opts, dest)
5619
5617
5620 if revs:
5618 if revs:
5621 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5619 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5622 if not revs:
5620 if not revs:
5623 raise error.Abort(
5621 raise error.Abort(
5624 _(b"specified revisions evaluate to an empty set"),
5622 _(b"specified revisions evaluate to an empty set"),
5625 hint=_(b"use different revision arguments"),
5623 hint=_(b"use different revision arguments"),
5626 )
5624 )
5627 elif path.pushrev:
5625 elif path.pushrev:
5628 # It doesn't make any sense to specify ancestor revisions. So limit
5626 # It doesn't make any sense to specify ancestor revisions. So limit
5629 # to DAG heads to make discovery simpler.
5627 # to DAG heads to make discovery simpler.
5630 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5628 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5631 revs = scmutil.revrange(repo, [expr])
5629 revs = scmutil.revrange(repo, [expr])
5632 revs = [repo[rev].node() for rev in revs]
5630 revs = [repo[rev].node() for rev in revs]
5633 if not revs:
5631 if not revs:
5634 raise error.Abort(
5632 raise error.Abort(
5635 _(b'default push revset for path evaluates to an empty set')
5633 _(b'default push revset for path evaluates to an empty set')
5636 )
5634 )
5637 elif ui.configbool(b'commands', b'push.require-revs'):
5635 elif ui.configbool(b'commands', b'push.require-revs'):
5638 raise error.Abort(
5636 raise error.Abort(
5639 _(b'no revisions specified to push'),
5637 _(b'no revisions specified to push'),
5640 hint=_(b'did you mean "hg push -r ."?'),
5638 hint=_(b'did you mean "hg push -r ."?'),
5641 )
5639 )
5642
5640
5643 repo._subtoppath = dest
5641 repo._subtoppath = dest
5644 try:
5642 try:
5645 # push subrepos depth-first for coherent ordering
5643 # push subrepos depth-first for coherent ordering
5646 c = repo[b'.']
5644 c = repo[b'.']
5647 subs = c.substate # only repos that are committed
5645 subs = c.substate # only repos that are committed
5648 for s in sorted(subs):
5646 for s in sorted(subs):
5649 result = c.sub(s).push(opts)
5647 result = c.sub(s).push(opts)
5650 if result == 0:
5648 if result == 0:
5651 return not result
5649 return not result
5652 finally:
5650 finally:
5653 del repo._subtoppath
5651 del repo._subtoppath
5654
5652
5655 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5653 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5656 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5654 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5657
5655
5658 pushop = exchange.push(
5656 pushop = exchange.push(
5659 repo,
5657 repo,
5660 other,
5658 other,
5661 opts.get(b'force'),
5659 opts.get(b'force'),
5662 revs=revs,
5660 revs=revs,
5663 newbranch=opts.get(b'new_branch'),
5661 newbranch=opts.get(b'new_branch'),
5664 bookmarks=opts.get(b'bookmark', ()),
5662 bookmarks=opts.get(b'bookmark', ()),
5665 publish=opts.get(b'publish'),
5663 publish=opts.get(b'publish'),
5666 opargs=opargs,
5664 opargs=opargs,
5667 )
5665 )
5668
5666
5669 result = not pushop.cgresult
5667 result = not pushop.cgresult
5670
5668
5671 if pushop.bkresult is not None:
5669 if pushop.bkresult is not None:
5672 if pushop.bkresult == 2:
5670 if pushop.bkresult == 2:
5673 result = 2
5671 result = 2
5674 elif not result and pushop.bkresult:
5672 elif not result and pushop.bkresult:
5675 result = 2
5673 result = 2
5676
5674
5677 return result
5675 return result
5678
5676
5679
5677
5680 @command(
5678 @command(
5681 b'recover',
5679 b'recover',
5682 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5680 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5683 helpcategory=command.CATEGORY_MAINTENANCE,
5681 helpcategory=command.CATEGORY_MAINTENANCE,
5684 )
5682 )
5685 def recover(ui, repo, **opts):
5683 def recover(ui, repo, **opts):
5686 """roll back an interrupted transaction
5684 """roll back an interrupted transaction
5687
5685
5688 Recover from an interrupted commit or pull.
5686 Recover from an interrupted commit or pull.
5689
5687
5690 This command tries to fix the repository status after an
5688 This command tries to fix the repository status after an
5691 interrupted operation. It should only be necessary when Mercurial
5689 interrupted operation. It should only be necessary when Mercurial
5692 suggests it.
5690 suggests it.
5693
5691
5694 Returns 0 if successful, 1 if nothing to recover or verify fails.
5692 Returns 0 if successful, 1 if nothing to recover or verify fails.
5695 """
5693 """
5696 ret = repo.recover()
5694 ret = repo.recover()
5697 if ret:
5695 if ret:
5698 if opts['verify']:
5696 if opts['verify']:
5699 return hg.verify(repo)
5697 return hg.verify(repo)
5700 else:
5698 else:
5701 msg = _(
5699 msg = _(
5702 b"(verify step skipped, run `hg verify` to check your "
5700 b"(verify step skipped, run `hg verify` to check your "
5703 b"repository content)\n"
5701 b"repository content)\n"
5704 )
5702 )
5705 ui.warn(msg)
5703 ui.warn(msg)
5706 return 0
5704 return 0
5707 return 1
5705 return 1
5708
5706
5709
5707
5710 @command(
5708 @command(
5711 b'remove|rm',
5709 b'remove|rm',
5712 [
5710 [
5713 (b'A', b'after', None, _(b'record delete for missing files')),
5711 (b'A', b'after', None, _(b'record delete for missing files')),
5714 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5712 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5715 ]
5713 ]
5716 + subrepoopts
5714 + subrepoopts
5717 + walkopts
5715 + walkopts
5718 + dryrunopts,
5716 + dryrunopts,
5719 _(b'[OPTION]... FILE...'),
5717 _(b'[OPTION]... FILE...'),
5720 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5718 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5721 helpbasic=True,
5719 helpbasic=True,
5722 inferrepo=True,
5720 inferrepo=True,
5723 )
5721 )
5724 def remove(ui, repo, *pats, **opts):
5722 def remove(ui, repo, *pats, **opts):
5725 """remove the specified files on the next commit
5723 """remove the specified files on the next commit
5726
5724
5727 Schedule the indicated files for removal from the current branch.
5725 Schedule the indicated files for removal from the current branch.
5728
5726
5729 This command schedules the files to be removed at the next commit.
5727 This command schedules the files to be removed at the next commit.
5730 To undo a remove before that, see :hg:`revert`. To undo added
5728 To undo a remove before that, see :hg:`revert`. To undo added
5731 files, see :hg:`forget`.
5729 files, see :hg:`forget`.
5732
5730
5733 .. container:: verbose
5731 .. container:: verbose
5734
5732
5735 -A/--after can be used to remove only files that have already
5733 -A/--after can be used to remove only files that have already
5736 been deleted, -f/--force can be used to force deletion, and -Af
5734 been deleted, -f/--force can be used to force deletion, and -Af
5737 can be used to remove files from the next revision without
5735 can be used to remove files from the next revision without
5738 deleting them from the working directory.
5736 deleting them from the working directory.
5739
5737
5740 The following table details the behavior of remove for different
5738 The following table details the behavior of remove for different
5741 file states (columns) and option combinations (rows). The file
5739 file states (columns) and option combinations (rows). The file
5742 states are Added [A], Clean [C], Modified [M] and Missing [!]
5740 states are Added [A], Clean [C], Modified [M] and Missing [!]
5743 (as reported by :hg:`status`). The actions are Warn, Remove
5741 (as reported by :hg:`status`). The actions are Warn, Remove
5744 (from branch) and Delete (from disk):
5742 (from branch) and Delete (from disk):
5745
5743
5746 ========= == == == ==
5744 ========= == == == ==
5747 opt/state A C M !
5745 opt/state A C M !
5748 ========= == == == ==
5746 ========= == == == ==
5749 none W RD W R
5747 none W RD W R
5750 -f R RD RD R
5748 -f R RD RD R
5751 -A W W W R
5749 -A W W W R
5752 -Af R R R R
5750 -Af R R R R
5753 ========= == == == ==
5751 ========= == == == ==
5754
5752
5755 .. note::
5753 .. note::
5756
5754
5757 :hg:`remove` never deletes files in Added [A] state from the
5755 :hg:`remove` never deletes files in Added [A] state from the
5758 working directory, not even if ``--force`` is specified.
5756 working directory, not even if ``--force`` is specified.
5759
5757
5760 Returns 0 on success, 1 if any warnings encountered.
5758 Returns 0 on success, 1 if any warnings encountered.
5761 """
5759 """
5762
5760
5763 opts = pycompat.byteskwargs(opts)
5761 opts = pycompat.byteskwargs(opts)
5764 after, force = opts.get(b'after'), opts.get(b'force')
5762 after, force = opts.get(b'after'), opts.get(b'force')
5765 dryrun = opts.get(b'dry_run')
5763 dryrun = opts.get(b'dry_run')
5766 if not pats and not after:
5764 if not pats and not after:
5767 raise error.Abort(_(b'no files specified'))
5765 raise error.Abort(_(b'no files specified'))
5768
5766
5769 m = scmutil.match(repo[None], pats, opts)
5767 m = scmutil.match(repo[None], pats, opts)
5770 subrepos = opts.get(b'subrepos')
5768 subrepos = opts.get(b'subrepos')
5771 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5769 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5772 return cmdutil.remove(
5770 return cmdutil.remove(
5773 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5771 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5774 )
5772 )
5775
5773
5776
5774
5777 @command(
5775 @command(
5778 b'rename|move|mv',
5776 b'rename|move|mv',
5779 [
5777 [
5780 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5778 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5781 (
5779 (
5782 b'f',
5780 b'f',
5783 b'force',
5781 b'force',
5784 None,
5782 None,
5785 _(b'forcibly move over an existing managed file'),
5783 _(b'forcibly move over an existing managed file'),
5786 ),
5784 ),
5787 ]
5785 ]
5788 + walkopts
5786 + walkopts
5789 + dryrunopts,
5787 + dryrunopts,
5790 _(b'[OPTION]... SOURCE... DEST'),
5788 _(b'[OPTION]... SOURCE... DEST'),
5791 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5789 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5792 )
5790 )
5793 def rename(ui, repo, *pats, **opts):
5791 def rename(ui, repo, *pats, **opts):
5794 """rename files; equivalent of copy + remove
5792 """rename files; equivalent of copy + remove
5795
5793
5796 Mark dest as copies of sources; mark sources for deletion. If dest
5794 Mark dest as copies of sources; mark sources for deletion. If dest
5797 is a directory, copies are put in that directory. If dest is a
5795 is a directory, copies are put in that directory. If dest is a
5798 file, there can only be one source.
5796 file, there can only be one source.
5799
5797
5800 By default, this command copies the contents of files as they
5798 By default, this command copies the contents of files as they
5801 exist in the working directory. If invoked with -A/--after, the
5799 exist in the working directory. If invoked with -A/--after, the
5802 operation is recorded, but no copying is performed.
5800 operation is recorded, but no copying is performed.
5803
5801
5804 This command takes effect at the next commit. To undo a rename
5802 This command takes effect at the next commit. To undo a rename
5805 before that, see :hg:`revert`.
5803 before that, see :hg:`revert`.
5806
5804
5807 Returns 0 on success, 1 if errors are encountered.
5805 Returns 0 on success, 1 if errors are encountered.
5808 """
5806 """
5809 opts = pycompat.byteskwargs(opts)
5807 opts = pycompat.byteskwargs(opts)
5810 with repo.wlock():
5808 with repo.wlock():
5811 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5809 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5812
5810
5813
5811
5814 @command(
5812 @command(
5815 b'resolve',
5813 b'resolve',
5816 [
5814 [
5817 (b'a', b'all', None, _(b'select all unresolved files')),
5815 (b'a', b'all', None, _(b'select all unresolved files')),
5818 (b'l', b'list', None, _(b'list state of files needing merge')),
5816 (b'l', b'list', None, _(b'list state of files needing merge')),
5819 (b'm', b'mark', None, _(b'mark files as resolved')),
5817 (b'm', b'mark', None, _(b'mark files as resolved')),
5820 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5818 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5821 (b'n', b'no-status', None, _(b'hide status prefix')),
5819 (b'n', b'no-status', None, _(b'hide status prefix')),
5822 (b'', b're-merge', None, _(b're-merge files')),
5820 (b'', b're-merge', None, _(b're-merge files')),
5823 ]
5821 ]
5824 + mergetoolopts
5822 + mergetoolopts
5825 + walkopts
5823 + walkopts
5826 + formatteropts,
5824 + formatteropts,
5827 _(b'[OPTION]... [FILE]...'),
5825 _(b'[OPTION]... [FILE]...'),
5828 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5826 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5829 inferrepo=True,
5827 inferrepo=True,
5830 )
5828 )
5831 def resolve(ui, repo, *pats, **opts):
5829 def resolve(ui, repo, *pats, **opts):
5832 """redo merges or set/view the merge status of files
5830 """redo merges or set/view the merge status of files
5833
5831
5834 Merges with unresolved conflicts are often the result of
5832 Merges with unresolved conflicts are often the result of
5835 non-interactive merging using the ``internal:merge`` configuration
5833 non-interactive merging using the ``internal:merge`` configuration
5836 setting, or a command-line merge tool like ``diff3``. The resolve
5834 setting, or a command-line merge tool like ``diff3``. The resolve
5837 command is used to manage the files involved in a merge, after
5835 command is used to manage the files involved in a merge, after
5838 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5836 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5839 working directory must have two parents). See :hg:`help
5837 working directory must have two parents). See :hg:`help
5840 merge-tools` for information on configuring merge tools.
5838 merge-tools` for information on configuring merge tools.
5841
5839
5842 The resolve command can be used in the following ways:
5840 The resolve command can be used in the following ways:
5843
5841
5844 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5842 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5845 the specified files, discarding any previous merge attempts. Re-merging
5843 the specified files, discarding any previous merge attempts. Re-merging
5846 is not performed for files already marked as resolved. Use ``--all/-a``
5844 is not performed for files already marked as resolved. Use ``--all/-a``
5847 to select all unresolved files. ``--tool`` can be used to specify
5845 to select all unresolved files. ``--tool`` can be used to specify
5848 the merge tool used for the given files. It overrides the HGMERGE
5846 the merge tool used for the given files. It overrides the HGMERGE
5849 environment variable and your configuration files. Previous file
5847 environment variable and your configuration files. Previous file
5850 contents are saved with a ``.orig`` suffix.
5848 contents are saved with a ``.orig`` suffix.
5851
5849
5852 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5850 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5853 (e.g. after having manually fixed-up the files). The default is
5851 (e.g. after having manually fixed-up the files). The default is
5854 to mark all unresolved files.
5852 to mark all unresolved files.
5855
5853
5856 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5854 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5857 default is to mark all resolved files.
5855 default is to mark all resolved files.
5858
5856
5859 - :hg:`resolve -l`: list files which had or still have conflicts.
5857 - :hg:`resolve -l`: list files which had or still have conflicts.
5860 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5858 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5861 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5859 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5862 the list. See :hg:`help filesets` for details.
5860 the list. See :hg:`help filesets` for details.
5863
5861
5864 .. note::
5862 .. note::
5865
5863
5866 Mercurial will not let you commit files with unresolved merge
5864 Mercurial will not let you commit files with unresolved merge
5867 conflicts. You must use :hg:`resolve -m ...` before you can
5865 conflicts. You must use :hg:`resolve -m ...` before you can
5868 commit after a conflicting merge.
5866 commit after a conflicting merge.
5869
5867
5870 .. container:: verbose
5868 .. container:: verbose
5871
5869
5872 Template:
5870 Template:
5873
5871
5874 The following keywords are supported in addition to the common template
5872 The following keywords are supported in addition to the common template
5875 keywords and functions. See also :hg:`help templates`.
5873 keywords and functions. See also :hg:`help templates`.
5876
5874
5877 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5875 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5878 :path: String. Repository-absolute path of the file.
5876 :path: String. Repository-absolute path of the file.
5879
5877
5880 Returns 0 on success, 1 if any files fail a resolve attempt.
5878 Returns 0 on success, 1 if any files fail a resolve attempt.
5881 """
5879 """
5882
5880
5883 opts = pycompat.byteskwargs(opts)
5881 opts = pycompat.byteskwargs(opts)
5884 confirm = ui.configbool(b'commands', b'resolve.confirm')
5882 confirm = ui.configbool(b'commands', b'resolve.confirm')
5885 flaglist = b'all mark unmark list no_status re_merge'.split()
5883 flaglist = b'all mark unmark list no_status re_merge'.split()
5886 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5884 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5887
5885
5888 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5886 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5889 if actioncount > 1:
5887 if actioncount > 1:
5890 raise error.Abort(_(b"too many actions specified"))
5888 raise error.Abort(_(b"too many actions specified"))
5891 elif actioncount == 0 and ui.configbool(
5889 elif actioncount == 0 and ui.configbool(
5892 b'commands', b'resolve.explicit-re-merge'
5890 b'commands', b'resolve.explicit-re-merge'
5893 ):
5891 ):
5894 hint = _(b'use --mark, --unmark, --list or --re-merge')
5892 hint = _(b'use --mark, --unmark, --list or --re-merge')
5895 raise error.Abort(_(b'no action specified'), hint=hint)
5893 raise error.Abort(_(b'no action specified'), hint=hint)
5896 if pats and all:
5894 if pats and all:
5897 raise error.Abort(_(b"can't specify --all and patterns"))
5895 raise error.Abort(_(b"can't specify --all and patterns"))
5898 if not (all or pats or show or mark or unmark):
5896 if not (all or pats or show or mark or unmark):
5899 raise error.Abort(
5897 raise error.Abort(
5900 _(b'no files or directories specified'),
5898 _(b'no files or directories specified'),
5901 hint=b'use --all to re-merge all unresolved files',
5899 hint=b'use --all to re-merge all unresolved files',
5902 )
5900 )
5903
5901
5904 if confirm:
5902 if confirm:
5905 if all:
5903 if all:
5906 if ui.promptchoice(
5904 if ui.promptchoice(
5907 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5905 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5908 ):
5906 ):
5909 raise error.Abort(_(b'user quit'))
5907 raise error.Abort(_(b'user quit'))
5910 if mark and not pats:
5908 if mark and not pats:
5911 if ui.promptchoice(
5909 if ui.promptchoice(
5912 _(
5910 _(
5913 b'mark all unresolved files as resolved (yn)?'
5911 b'mark all unresolved files as resolved (yn)?'
5914 b'$$ &Yes $$ &No'
5912 b'$$ &Yes $$ &No'
5915 )
5913 )
5916 ):
5914 ):
5917 raise error.Abort(_(b'user quit'))
5915 raise error.Abort(_(b'user quit'))
5918 if unmark and not pats:
5916 if unmark and not pats:
5919 if ui.promptchoice(
5917 if ui.promptchoice(
5920 _(
5918 _(
5921 b'mark all resolved files as unresolved (yn)?'
5919 b'mark all resolved files as unresolved (yn)?'
5922 b'$$ &Yes $$ &No'
5920 b'$$ &Yes $$ &No'
5923 )
5921 )
5924 ):
5922 ):
5925 raise error.Abort(_(b'user quit'))
5923 raise error.Abort(_(b'user quit'))
5926
5924
5927 uipathfn = scmutil.getuipathfn(repo)
5925 uipathfn = scmutil.getuipathfn(repo)
5928
5926
5929 if show:
5927 if show:
5930 ui.pager(b'resolve')
5928 ui.pager(b'resolve')
5931 fm = ui.formatter(b'resolve', opts)
5929 fm = ui.formatter(b'resolve', opts)
5932 ms = mergemod.mergestate.read(repo)
5930 ms = mergemod.mergestate.read(repo)
5933 wctx = repo[None]
5931 wctx = repo[None]
5934 m = scmutil.match(wctx, pats, opts)
5932 m = scmutil.match(wctx, pats, opts)
5935
5933
5936 # Labels and keys based on merge state. Unresolved path conflicts show
5934 # Labels and keys based on merge state. Unresolved path conflicts show
5937 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5935 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5938 # resolved conflicts.
5936 # resolved conflicts.
5939 mergestateinfo = {
5937 mergestateinfo = {
5940 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5938 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5941 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5939 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5942 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5940 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5943 b'resolve.unresolved',
5941 b'resolve.unresolved',
5944 b'P',
5942 b'P',
5945 ),
5943 ),
5946 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5944 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5947 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5945 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5948 b'resolve.driverresolved',
5946 b'resolve.driverresolved',
5949 b'D',
5947 b'D',
5950 ),
5948 ),
5951 }
5949 }
5952
5950
5953 for f in ms:
5951 for f in ms:
5954 if not m(f):
5952 if not m(f):
5955 continue
5953 continue
5956
5954
5957 if ms[f] == mergemod.MERGE_RECORD_MERGED_OTHER:
5955 if ms[f] == mergemod.MERGE_RECORD_MERGED_OTHER:
5958 continue
5956 continue
5959 label, key = mergestateinfo[ms[f]]
5957 label, key = mergestateinfo[ms[f]]
5960 fm.startitem()
5958 fm.startitem()
5961 fm.context(ctx=wctx)
5959 fm.context(ctx=wctx)
5962 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5960 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5963 fm.data(path=f)
5961 fm.data(path=f)
5964 fm.plain(b'%s\n' % uipathfn(f), label=label)
5962 fm.plain(b'%s\n' % uipathfn(f), label=label)
5965 fm.end()
5963 fm.end()
5966 return 0
5964 return 0
5967
5965
5968 with repo.wlock():
5966 with repo.wlock():
5969 ms = mergemod.mergestate.read(repo)
5967 ms = mergemod.mergestate.read(repo)
5970
5968
5971 if not (ms.active() or repo.dirstate.p2() != nullid):
5969 if not (ms.active() or repo.dirstate.p2() != nullid):
5972 raise error.Abort(
5970 raise error.Abort(
5973 _(b'resolve command not applicable when not merging')
5971 _(b'resolve command not applicable when not merging')
5974 )
5972 )
5975
5973
5976 wctx = repo[None]
5974 wctx = repo[None]
5977
5975
5978 if (
5976 if (
5979 ms.mergedriver
5977 ms.mergedriver
5980 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5978 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5981 ):
5979 ):
5982 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5980 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5983 ms.commit()
5981 ms.commit()
5984 # allow mark and unmark to go through
5982 # allow mark and unmark to go through
5985 if not mark and not unmark and not proceed:
5983 if not mark and not unmark and not proceed:
5986 return 1
5984 return 1
5987
5985
5988 m = scmutil.match(wctx, pats, opts)
5986 m = scmutil.match(wctx, pats, opts)
5989 ret = 0
5987 ret = 0
5990 didwork = False
5988 didwork = False
5991 runconclude = False
5989 runconclude = False
5992
5990
5993 tocomplete = []
5991 tocomplete = []
5994 hasconflictmarkers = []
5992 hasconflictmarkers = []
5995 if mark:
5993 if mark:
5996 markcheck = ui.config(b'commands', b'resolve.mark-check')
5994 markcheck = ui.config(b'commands', b'resolve.mark-check')
5997 if markcheck not in [b'warn', b'abort']:
5995 if markcheck not in [b'warn', b'abort']:
5998 # Treat all invalid / unrecognized values as 'none'.
5996 # Treat all invalid / unrecognized values as 'none'.
5999 markcheck = False
5997 markcheck = False
6000 for f in ms:
5998 for f in ms:
6001 if not m(f):
5999 if not m(f):
6002 continue
6000 continue
6003
6001
6004 didwork = True
6002 didwork = True
6005
6003
6006 if ms[f] == mergemod.MERGE_RECORD_MERGED_OTHER:
6004 if ms[f] == mergemod.MERGE_RECORD_MERGED_OTHER:
6007 continue
6005 continue
6008
6006
6009 # don't let driver-resolved files be marked, and run the conclude
6007 # don't let driver-resolved files be marked, and run the conclude
6010 # step if asked to resolve
6008 # step if asked to resolve
6011 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6009 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6012 exact = m.exact(f)
6010 exact = m.exact(f)
6013 if mark:
6011 if mark:
6014 if exact:
6012 if exact:
6015 ui.warn(
6013 ui.warn(
6016 _(b'not marking %s as it is driver-resolved\n')
6014 _(b'not marking %s as it is driver-resolved\n')
6017 % uipathfn(f)
6015 % uipathfn(f)
6018 )
6016 )
6019 elif unmark:
6017 elif unmark:
6020 if exact:
6018 if exact:
6021 ui.warn(
6019 ui.warn(
6022 _(b'not unmarking %s as it is driver-resolved\n')
6020 _(b'not unmarking %s as it is driver-resolved\n')
6023 % uipathfn(f)
6021 % uipathfn(f)
6024 )
6022 )
6025 else:
6023 else:
6026 runconclude = True
6024 runconclude = True
6027 continue
6025 continue
6028
6026
6029 # path conflicts must be resolved manually
6027 # path conflicts must be resolved manually
6030 if ms[f] in (
6028 if ms[f] in (
6031 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6029 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6032 mergemod.MERGE_RECORD_RESOLVED_PATH,
6030 mergemod.MERGE_RECORD_RESOLVED_PATH,
6033 ):
6031 ):
6034 if mark:
6032 if mark:
6035 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6033 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6036 elif unmark:
6034 elif unmark:
6037 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6035 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6038 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6036 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6039 ui.warn(
6037 ui.warn(
6040 _(b'%s: path conflict must be resolved manually\n')
6038 _(b'%s: path conflict must be resolved manually\n')
6041 % uipathfn(f)
6039 % uipathfn(f)
6042 )
6040 )
6043 continue
6041 continue
6044
6042
6045 if mark:
6043 if mark:
6046 if markcheck:
6044 if markcheck:
6047 fdata = repo.wvfs.tryread(f)
6045 fdata = repo.wvfs.tryread(f)
6048 if (
6046 if (
6049 filemerge.hasconflictmarkers(fdata)
6047 filemerge.hasconflictmarkers(fdata)
6050 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6048 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6051 ):
6049 ):
6052 hasconflictmarkers.append(f)
6050 hasconflictmarkers.append(f)
6053 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6051 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6054 elif unmark:
6052 elif unmark:
6055 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6053 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6056 else:
6054 else:
6057 # backup pre-resolve (merge uses .orig for its own purposes)
6055 # backup pre-resolve (merge uses .orig for its own purposes)
6058 a = repo.wjoin(f)
6056 a = repo.wjoin(f)
6059 try:
6057 try:
6060 util.copyfile(a, a + b".resolve")
6058 util.copyfile(a, a + b".resolve")
6061 except (IOError, OSError) as inst:
6059 except (IOError, OSError) as inst:
6062 if inst.errno != errno.ENOENT:
6060 if inst.errno != errno.ENOENT:
6063 raise
6061 raise
6064
6062
6065 try:
6063 try:
6066 # preresolve file
6064 # preresolve file
6067 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6065 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6068 with ui.configoverride(overrides, b'resolve'):
6066 with ui.configoverride(overrides, b'resolve'):
6069 complete, r = ms.preresolve(f, wctx)
6067 complete, r = ms.preresolve(f, wctx)
6070 if not complete:
6068 if not complete:
6071 tocomplete.append(f)
6069 tocomplete.append(f)
6072 elif r:
6070 elif r:
6073 ret = 1
6071 ret = 1
6074 finally:
6072 finally:
6075 ms.commit()
6073 ms.commit()
6076
6074
6077 # replace filemerge's .orig file with our resolve file, but only
6075 # replace filemerge's .orig file with our resolve file, but only
6078 # for merges that are complete
6076 # for merges that are complete
6079 if complete:
6077 if complete:
6080 try:
6078 try:
6081 util.rename(
6079 util.rename(
6082 a + b".resolve", scmutil.backuppath(ui, repo, f)
6080 a + b".resolve", scmutil.backuppath(ui, repo, f)
6083 )
6081 )
6084 except OSError as inst:
6082 except OSError as inst:
6085 if inst.errno != errno.ENOENT:
6083 if inst.errno != errno.ENOENT:
6086 raise
6084 raise
6087
6085
6088 if hasconflictmarkers:
6086 if hasconflictmarkers:
6089 ui.warn(
6087 ui.warn(
6090 _(
6088 _(
6091 b'warning: the following files still have conflict '
6089 b'warning: the following files still have conflict '
6092 b'markers:\n'
6090 b'markers:\n'
6093 )
6091 )
6094 + b''.join(
6092 + b''.join(
6095 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6093 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6096 )
6094 )
6097 )
6095 )
6098 if markcheck == b'abort' and not all and not pats:
6096 if markcheck == b'abort' and not all and not pats:
6099 raise error.Abort(
6097 raise error.Abort(
6100 _(b'conflict markers detected'),
6098 _(b'conflict markers detected'),
6101 hint=_(b'use --all to mark anyway'),
6099 hint=_(b'use --all to mark anyway'),
6102 )
6100 )
6103
6101
6104 for f in tocomplete:
6102 for f in tocomplete:
6105 try:
6103 try:
6106 # resolve file
6104 # resolve file
6107 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6105 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6108 with ui.configoverride(overrides, b'resolve'):
6106 with ui.configoverride(overrides, b'resolve'):
6109 r = ms.resolve(f, wctx)
6107 r = ms.resolve(f, wctx)
6110 if r:
6108 if r:
6111 ret = 1
6109 ret = 1
6112 finally:
6110 finally:
6113 ms.commit()
6111 ms.commit()
6114
6112
6115 # replace filemerge's .orig file with our resolve file
6113 # replace filemerge's .orig file with our resolve file
6116 a = repo.wjoin(f)
6114 a = repo.wjoin(f)
6117 try:
6115 try:
6118 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6116 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6119 except OSError as inst:
6117 except OSError as inst:
6120 if inst.errno != errno.ENOENT:
6118 if inst.errno != errno.ENOENT:
6121 raise
6119 raise
6122
6120
6123 ms.commit()
6121 ms.commit()
6124 ms.recordactions()
6122 ms.recordactions()
6125
6123
6126 if not didwork and pats:
6124 if not didwork and pats:
6127 hint = None
6125 hint = None
6128 if not any([p for p in pats if p.find(b':') >= 0]):
6126 if not any([p for p in pats if p.find(b':') >= 0]):
6129 pats = [b'path:%s' % p for p in pats]
6127 pats = [b'path:%s' % p for p in pats]
6130 m = scmutil.match(wctx, pats, opts)
6128 m = scmutil.match(wctx, pats, opts)
6131 for f in ms:
6129 for f in ms:
6132 if not m(f):
6130 if not m(f):
6133 continue
6131 continue
6134
6132
6135 def flag(o):
6133 def flag(o):
6136 if o == b're_merge':
6134 if o == b're_merge':
6137 return b'--re-merge '
6135 return b'--re-merge '
6138 return b'-%s ' % o[0:1]
6136 return b'-%s ' % o[0:1]
6139
6137
6140 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6138 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6141 hint = _(b"(try: hg resolve %s%s)\n") % (
6139 hint = _(b"(try: hg resolve %s%s)\n") % (
6142 flags,
6140 flags,
6143 b' '.join(pats),
6141 b' '.join(pats),
6144 )
6142 )
6145 break
6143 break
6146 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6144 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6147 if hint:
6145 if hint:
6148 ui.warn(hint)
6146 ui.warn(hint)
6149 elif ms.mergedriver and ms.mdstate() != b's':
6147 elif ms.mergedriver and ms.mdstate() != b's':
6150 # run conclude step when either a driver-resolved file is requested
6148 # run conclude step when either a driver-resolved file is requested
6151 # or there are no driver-resolved files
6149 # or there are no driver-resolved files
6152 # we can't use 'ret' to determine whether any files are unresolved
6150 # we can't use 'ret' to determine whether any files are unresolved
6153 # because we might not have tried to resolve some
6151 # because we might not have tried to resolve some
6154 if (runconclude or not list(ms.driverresolved())) and not list(
6152 if (runconclude or not list(ms.driverresolved())) and not list(
6155 ms.unresolved()
6153 ms.unresolved()
6156 ):
6154 ):
6157 proceed = mergemod.driverconclude(repo, ms, wctx)
6155 proceed = mergemod.driverconclude(repo, ms, wctx)
6158 ms.commit()
6156 ms.commit()
6159 if not proceed:
6157 if not proceed:
6160 return 1
6158 return 1
6161
6159
6162 # Nudge users into finishing an unfinished operation
6160 # Nudge users into finishing an unfinished operation
6163 unresolvedf = list(ms.unresolved())
6161 unresolvedf = list(ms.unresolved())
6164 driverresolvedf = list(ms.driverresolved())
6162 driverresolvedf = list(ms.driverresolved())
6165 if not unresolvedf and not driverresolvedf:
6163 if not unresolvedf and not driverresolvedf:
6166 ui.status(_(b'(no more unresolved files)\n'))
6164 ui.status(_(b'(no more unresolved files)\n'))
6167 cmdutil.checkafterresolved(repo)
6165 cmdutil.checkafterresolved(repo)
6168 elif not unresolvedf:
6166 elif not unresolvedf:
6169 ui.status(
6167 ui.status(
6170 _(
6168 _(
6171 b'(no more unresolved files -- '
6169 b'(no more unresolved files -- '
6172 b'run "hg resolve --all" to conclude)\n'
6170 b'run "hg resolve --all" to conclude)\n'
6173 )
6171 )
6174 )
6172 )
6175
6173
6176 return ret
6174 return ret
6177
6175
6178
6176
6179 @command(
6177 @command(
6180 b'revert',
6178 b'revert',
6181 [
6179 [
6182 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6180 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6183 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6181 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6184 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6182 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6185 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6183 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6186 (b'i', b'interactive', None, _(b'interactively select the changes')),
6184 (b'i', b'interactive', None, _(b'interactively select the changes')),
6187 ]
6185 ]
6188 + walkopts
6186 + walkopts
6189 + dryrunopts,
6187 + dryrunopts,
6190 _(b'[OPTION]... [-r REV] [NAME]...'),
6188 _(b'[OPTION]... [-r REV] [NAME]...'),
6191 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6189 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6192 )
6190 )
6193 def revert(ui, repo, *pats, **opts):
6191 def revert(ui, repo, *pats, **opts):
6194 """restore files to their checkout state
6192 """restore files to their checkout state
6195
6193
6196 .. note::
6194 .. note::
6197
6195
6198 To check out earlier revisions, you should use :hg:`update REV`.
6196 To check out earlier revisions, you should use :hg:`update REV`.
6199 To cancel an uncommitted merge (and lose your changes),
6197 To cancel an uncommitted merge (and lose your changes),
6200 use :hg:`merge --abort`.
6198 use :hg:`merge --abort`.
6201
6199
6202 With no revision specified, revert the specified files or directories
6200 With no revision specified, revert the specified files or directories
6203 to the contents they had in the parent of the working directory.
6201 to the contents they had in the parent of the working directory.
6204 This restores the contents of files to an unmodified
6202 This restores the contents of files to an unmodified
6205 state and unschedules adds, removes, copies, and renames. If the
6203 state and unschedules adds, removes, copies, and renames. If the
6206 working directory has two parents, you must explicitly specify a
6204 working directory has two parents, you must explicitly specify a
6207 revision.
6205 revision.
6208
6206
6209 Using the -r/--rev or -d/--date options, revert the given files or
6207 Using the -r/--rev or -d/--date options, revert the given files or
6210 directories to their states as of a specific revision. Because
6208 directories to their states as of a specific revision. Because
6211 revert does not change the working directory parents, this will
6209 revert does not change the working directory parents, this will
6212 cause these files to appear modified. This can be helpful to "back
6210 cause these files to appear modified. This can be helpful to "back
6213 out" some or all of an earlier change. See :hg:`backout` for a
6211 out" some or all of an earlier change. See :hg:`backout` for a
6214 related method.
6212 related method.
6215
6213
6216 Modified files are saved with a .orig suffix before reverting.
6214 Modified files are saved with a .orig suffix before reverting.
6217 To disable these backups, use --no-backup. It is possible to store
6215 To disable these backups, use --no-backup. It is possible to store
6218 the backup files in a custom directory relative to the root of the
6216 the backup files in a custom directory relative to the root of the
6219 repository by setting the ``ui.origbackuppath`` configuration
6217 repository by setting the ``ui.origbackuppath`` configuration
6220 option.
6218 option.
6221
6219
6222 See :hg:`help dates` for a list of formats valid for -d/--date.
6220 See :hg:`help dates` for a list of formats valid for -d/--date.
6223
6221
6224 See :hg:`help backout` for a way to reverse the effect of an
6222 See :hg:`help backout` for a way to reverse the effect of an
6225 earlier changeset.
6223 earlier changeset.
6226
6224
6227 Returns 0 on success.
6225 Returns 0 on success.
6228 """
6226 """
6229
6227
6230 opts = pycompat.byteskwargs(opts)
6228 opts = pycompat.byteskwargs(opts)
6231 if opts.get(b"date"):
6229 if opts.get(b"date"):
6232 if opts.get(b"rev"):
6230 if opts.get(b"rev"):
6233 raise error.Abort(_(b"you can't specify a revision and a date"))
6231 raise error.Abort(_(b"you can't specify a revision and a date"))
6234 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6232 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6235
6233
6236 parent, p2 = repo.dirstate.parents()
6234 parent, p2 = repo.dirstate.parents()
6237 if not opts.get(b'rev') and p2 != nullid:
6235 if not opts.get(b'rev') and p2 != nullid:
6238 # revert after merge is a trap for new users (issue2915)
6236 # revert after merge is a trap for new users (issue2915)
6239 raise error.Abort(
6237 raise error.Abort(
6240 _(b'uncommitted merge with no revision specified'),
6238 _(b'uncommitted merge with no revision specified'),
6241 hint=_(b"use 'hg update' or see 'hg help revert'"),
6239 hint=_(b"use 'hg update' or see 'hg help revert'"),
6242 )
6240 )
6243
6241
6244 rev = opts.get(b'rev')
6242 rev = opts.get(b'rev')
6245 if rev:
6243 if rev:
6246 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6244 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6247 ctx = scmutil.revsingle(repo, rev)
6245 ctx = scmutil.revsingle(repo, rev)
6248
6246
6249 if not (
6247 if not (
6250 pats
6248 pats
6251 or opts.get(b'include')
6249 or opts.get(b'include')
6252 or opts.get(b'exclude')
6250 or opts.get(b'exclude')
6253 or opts.get(b'all')
6251 or opts.get(b'all')
6254 or opts.get(b'interactive')
6252 or opts.get(b'interactive')
6255 ):
6253 ):
6256 msg = _(b"no files or directories specified")
6254 msg = _(b"no files or directories specified")
6257 if p2 != nullid:
6255 if p2 != nullid:
6258 hint = _(
6256 hint = _(
6259 b"uncommitted merge, use --all to discard all changes,"
6257 b"uncommitted merge, use --all to discard all changes,"
6260 b" or 'hg update -C .' to abort the merge"
6258 b" or 'hg update -C .' to abort the merge"
6261 )
6259 )
6262 raise error.Abort(msg, hint=hint)
6260 raise error.Abort(msg, hint=hint)
6263 dirty = any(repo.status())
6261 dirty = any(repo.status())
6264 node = ctx.node()
6262 node = ctx.node()
6265 if node != parent:
6263 if node != parent:
6266 if dirty:
6264 if dirty:
6267 hint = (
6265 hint = (
6268 _(
6266 _(
6269 b"uncommitted changes, use --all to discard all"
6267 b"uncommitted changes, use --all to discard all"
6270 b" changes, or 'hg update %d' to update"
6268 b" changes, or 'hg update %d' to update"
6271 )
6269 )
6272 % ctx.rev()
6270 % ctx.rev()
6273 )
6271 )
6274 else:
6272 else:
6275 hint = (
6273 hint = (
6276 _(
6274 _(
6277 b"use --all to revert all files,"
6275 b"use --all to revert all files,"
6278 b" or 'hg update %d' to update"
6276 b" or 'hg update %d' to update"
6279 )
6277 )
6280 % ctx.rev()
6278 % ctx.rev()
6281 )
6279 )
6282 elif dirty:
6280 elif dirty:
6283 hint = _(b"uncommitted changes, use --all to discard all changes")
6281 hint = _(b"uncommitted changes, use --all to discard all changes")
6284 else:
6282 else:
6285 hint = _(b"use --all to revert all files")
6283 hint = _(b"use --all to revert all files")
6286 raise error.Abort(msg, hint=hint)
6284 raise error.Abort(msg, hint=hint)
6287
6285
6288 return cmdutil.revert(
6286 return cmdutil.revert(
6289 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6287 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6290 )
6288 )
6291
6289
6292
6290
6293 @command(
6291 @command(
6294 b'rollback',
6292 b'rollback',
6295 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6293 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6296 helpcategory=command.CATEGORY_MAINTENANCE,
6294 helpcategory=command.CATEGORY_MAINTENANCE,
6297 )
6295 )
6298 def rollback(ui, repo, **opts):
6296 def rollback(ui, repo, **opts):
6299 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6297 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6300
6298
6301 Please use :hg:`commit --amend` instead of rollback to correct
6299 Please use :hg:`commit --amend` instead of rollback to correct
6302 mistakes in the last commit.
6300 mistakes in the last commit.
6303
6301
6304 This command should be used with care. There is only one level of
6302 This command should be used with care. There is only one level of
6305 rollback, and there is no way to undo a rollback. It will also
6303 rollback, and there is no way to undo a rollback. It will also
6306 restore the dirstate at the time of the last transaction, losing
6304 restore the dirstate at the time of the last transaction, losing
6307 any dirstate changes since that time. This command does not alter
6305 any dirstate changes since that time. This command does not alter
6308 the working directory.
6306 the working directory.
6309
6307
6310 Transactions are used to encapsulate the effects of all commands
6308 Transactions are used to encapsulate the effects of all commands
6311 that create new changesets or propagate existing changesets into a
6309 that create new changesets or propagate existing changesets into a
6312 repository.
6310 repository.
6313
6311
6314 .. container:: verbose
6312 .. container:: verbose
6315
6313
6316 For example, the following commands are transactional, and their
6314 For example, the following commands are transactional, and their
6317 effects can be rolled back:
6315 effects can be rolled back:
6318
6316
6319 - commit
6317 - commit
6320 - import
6318 - import
6321 - pull
6319 - pull
6322 - push (with this repository as the destination)
6320 - push (with this repository as the destination)
6323 - unbundle
6321 - unbundle
6324
6322
6325 To avoid permanent data loss, rollback will refuse to rollback a
6323 To avoid permanent data loss, rollback will refuse to rollback a
6326 commit transaction if it isn't checked out. Use --force to
6324 commit transaction if it isn't checked out. Use --force to
6327 override this protection.
6325 override this protection.
6328
6326
6329 The rollback command can be entirely disabled by setting the
6327 The rollback command can be entirely disabled by setting the
6330 ``ui.rollback`` configuration setting to false. If you're here
6328 ``ui.rollback`` configuration setting to false. If you're here
6331 because you want to use rollback and it's disabled, you can
6329 because you want to use rollback and it's disabled, you can
6332 re-enable the command by setting ``ui.rollback`` to true.
6330 re-enable the command by setting ``ui.rollback`` to true.
6333
6331
6334 This command is not intended for use on public repositories. Once
6332 This command is not intended for use on public repositories. Once
6335 changes are visible for pull by other users, rolling a transaction
6333 changes are visible for pull by other users, rolling a transaction
6336 back locally is ineffective (someone else may already have pulled
6334 back locally is ineffective (someone else may already have pulled
6337 the changes). Furthermore, a race is possible with readers of the
6335 the changes). Furthermore, a race is possible with readers of the
6338 repository; for example an in-progress pull from the repository
6336 repository; for example an in-progress pull from the repository
6339 may fail if a rollback is performed.
6337 may fail if a rollback is performed.
6340
6338
6341 Returns 0 on success, 1 if no rollback data is available.
6339 Returns 0 on success, 1 if no rollback data is available.
6342 """
6340 """
6343 if not ui.configbool(b'ui', b'rollback'):
6341 if not ui.configbool(b'ui', b'rollback'):
6344 raise error.Abort(
6342 raise error.Abort(
6345 _(b'rollback is disabled because it is unsafe'),
6343 _(b'rollback is disabled because it is unsafe'),
6346 hint=b'see `hg help -v rollback` for information',
6344 hint=b'see `hg help -v rollback` for information',
6347 )
6345 )
6348 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6346 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6349
6347
6350
6348
6351 @command(
6349 @command(
6352 b'root',
6350 b'root',
6353 [] + formatteropts,
6351 [] + formatteropts,
6354 intents={INTENT_READONLY},
6352 intents={INTENT_READONLY},
6355 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6353 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6356 )
6354 )
6357 def root(ui, repo, **opts):
6355 def root(ui, repo, **opts):
6358 """print the root (top) of the current working directory
6356 """print the root (top) of the current working directory
6359
6357
6360 Print the root directory of the current repository.
6358 Print the root directory of the current repository.
6361
6359
6362 .. container:: verbose
6360 .. container:: verbose
6363
6361
6364 Template:
6362 Template:
6365
6363
6366 The following keywords are supported in addition to the common template
6364 The following keywords are supported in addition to the common template
6367 keywords and functions. See also :hg:`help templates`.
6365 keywords and functions. See also :hg:`help templates`.
6368
6366
6369 :hgpath: String. Path to the .hg directory.
6367 :hgpath: String. Path to the .hg directory.
6370 :storepath: String. Path to the directory holding versioned data.
6368 :storepath: String. Path to the directory holding versioned data.
6371
6369
6372 Returns 0 on success.
6370 Returns 0 on success.
6373 """
6371 """
6374 opts = pycompat.byteskwargs(opts)
6372 opts = pycompat.byteskwargs(opts)
6375 with ui.formatter(b'root', opts) as fm:
6373 with ui.formatter(b'root', opts) as fm:
6376 fm.startitem()
6374 fm.startitem()
6377 fm.write(b'reporoot', b'%s\n', repo.root)
6375 fm.write(b'reporoot', b'%s\n', repo.root)
6378 fm.data(hgpath=repo.path, storepath=repo.spath)
6376 fm.data(hgpath=repo.path, storepath=repo.spath)
6379
6377
6380
6378
6381 @command(
6379 @command(
6382 b'serve',
6380 b'serve',
6383 [
6381 [
6384 (
6382 (
6385 b'A',
6383 b'A',
6386 b'accesslog',
6384 b'accesslog',
6387 b'',
6385 b'',
6388 _(b'name of access log file to write to'),
6386 _(b'name of access log file to write to'),
6389 _(b'FILE'),
6387 _(b'FILE'),
6390 ),
6388 ),
6391 (b'd', b'daemon', None, _(b'run server in background')),
6389 (b'd', b'daemon', None, _(b'run server in background')),
6392 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6390 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6393 (
6391 (
6394 b'E',
6392 b'E',
6395 b'errorlog',
6393 b'errorlog',
6396 b'',
6394 b'',
6397 _(b'name of error log file to write to'),
6395 _(b'name of error log file to write to'),
6398 _(b'FILE'),
6396 _(b'FILE'),
6399 ),
6397 ),
6400 # use string type, then we can check if something was passed
6398 # use string type, then we can check if something was passed
6401 (
6399 (
6402 b'p',
6400 b'p',
6403 b'port',
6401 b'port',
6404 b'',
6402 b'',
6405 _(b'port to listen on (default: 8000)'),
6403 _(b'port to listen on (default: 8000)'),
6406 _(b'PORT'),
6404 _(b'PORT'),
6407 ),
6405 ),
6408 (
6406 (
6409 b'a',
6407 b'a',
6410 b'address',
6408 b'address',
6411 b'',
6409 b'',
6412 _(b'address to listen on (default: all interfaces)'),
6410 _(b'address to listen on (default: all interfaces)'),
6413 _(b'ADDR'),
6411 _(b'ADDR'),
6414 ),
6412 ),
6415 (
6413 (
6416 b'',
6414 b'',
6417 b'prefix',
6415 b'prefix',
6418 b'',
6416 b'',
6419 _(b'prefix path to serve from (default: server root)'),
6417 _(b'prefix path to serve from (default: server root)'),
6420 _(b'PREFIX'),
6418 _(b'PREFIX'),
6421 ),
6419 ),
6422 (
6420 (
6423 b'n',
6421 b'n',
6424 b'name',
6422 b'name',
6425 b'',
6423 b'',
6426 _(b'name to show in web pages (default: working directory)'),
6424 _(b'name to show in web pages (default: working directory)'),
6427 _(b'NAME'),
6425 _(b'NAME'),
6428 ),
6426 ),
6429 (
6427 (
6430 b'',
6428 b'',
6431 b'web-conf',
6429 b'web-conf',
6432 b'',
6430 b'',
6433 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6431 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6434 _(b'FILE'),
6432 _(b'FILE'),
6435 ),
6433 ),
6436 (
6434 (
6437 b'',
6435 b'',
6438 b'webdir-conf',
6436 b'webdir-conf',
6439 b'',
6437 b'',
6440 _(b'name of the hgweb config file (DEPRECATED)'),
6438 _(b'name of the hgweb config file (DEPRECATED)'),
6441 _(b'FILE'),
6439 _(b'FILE'),
6442 ),
6440 ),
6443 (
6441 (
6444 b'',
6442 b'',
6445 b'pid-file',
6443 b'pid-file',
6446 b'',
6444 b'',
6447 _(b'name of file to write process ID to'),
6445 _(b'name of file to write process ID to'),
6448 _(b'FILE'),
6446 _(b'FILE'),
6449 ),
6447 ),
6450 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6448 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6451 (
6449 (
6452 b'',
6450 b'',
6453 b'cmdserver',
6451 b'cmdserver',
6454 b'',
6452 b'',
6455 _(b'for remote clients (ADVANCED)'),
6453 _(b'for remote clients (ADVANCED)'),
6456 _(b'MODE'),
6454 _(b'MODE'),
6457 ),
6455 ),
6458 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6456 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6459 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6457 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6460 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6458 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6461 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6459 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6462 (b'', b'print-url', None, _(b'start and print only the URL')),
6460 (b'', b'print-url', None, _(b'start and print only the URL')),
6463 ]
6461 ]
6464 + subrepoopts,
6462 + subrepoopts,
6465 _(b'[OPTION]...'),
6463 _(b'[OPTION]...'),
6466 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6464 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6467 helpbasic=True,
6465 helpbasic=True,
6468 optionalrepo=True,
6466 optionalrepo=True,
6469 )
6467 )
6470 def serve(ui, repo, **opts):
6468 def serve(ui, repo, **opts):
6471 """start stand-alone webserver
6469 """start stand-alone webserver
6472
6470
6473 Start a local HTTP repository browser and pull server. You can use
6471 Start a local HTTP repository browser and pull server. You can use
6474 this for ad-hoc sharing and browsing of repositories. It is
6472 this for ad-hoc sharing and browsing of repositories. It is
6475 recommended to use a real web server to serve a repository for
6473 recommended to use a real web server to serve a repository for
6476 longer periods of time.
6474 longer periods of time.
6477
6475
6478 Please note that the server does not implement access control.
6476 Please note that the server does not implement access control.
6479 This means that, by default, anybody can read from the server and
6477 This means that, by default, anybody can read from the server and
6480 nobody can write to it by default. Set the ``web.allow-push``
6478 nobody can write to it by default. Set the ``web.allow-push``
6481 option to ``*`` to allow everybody to push to the server. You
6479 option to ``*`` to allow everybody to push to the server. You
6482 should use a real web server if you need to authenticate users.
6480 should use a real web server if you need to authenticate users.
6483
6481
6484 By default, the server logs accesses to stdout and errors to
6482 By default, the server logs accesses to stdout and errors to
6485 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6483 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6486 files.
6484 files.
6487
6485
6488 To have the server choose a free port number to listen on, specify
6486 To have the server choose a free port number to listen on, specify
6489 a port number of 0; in this case, the server will print the port
6487 a port number of 0; in this case, the server will print the port
6490 number it uses.
6488 number it uses.
6491
6489
6492 Returns 0 on success.
6490 Returns 0 on success.
6493 """
6491 """
6494
6492
6495 opts = pycompat.byteskwargs(opts)
6493 opts = pycompat.byteskwargs(opts)
6496 if opts[b"stdio"] and opts[b"cmdserver"]:
6494 if opts[b"stdio"] and opts[b"cmdserver"]:
6497 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6495 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6498 if opts[b"print_url"] and ui.verbose:
6496 if opts[b"print_url"] and ui.verbose:
6499 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6497 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6500
6498
6501 if opts[b"stdio"]:
6499 if opts[b"stdio"]:
6502 if repo is None:
6500 if repo is None:
6503 raise error.RepoError(
6501 raise error.RepoError(
6504 _(b"there is no Mercurial repository here (.hg not found)")
6502 _(b"there is no Mercurial repository here (.hg not found)")
6505 )
6503 )
6506 s = wireprotoserver.sshserver(ui, repo)
6504 s = wireprotoserver.sshserver(ui, repo)
6507 s.serve_forever()
6505 s.serve_forever()
6508
6506
6509 service = server.createservice(ui, repo, opts)
6507 service = server.createservice(ui, repo, opts)
6510 return server.runservice(opts, initfn=service.init, runfn=service.run)
6508 return server.runservice(opts, initfn=service.init, runfn=service.run)
6511
6509
6512
6510
6513 @command(
6511 @command(
6514 b'shelve',
6512 b'shelve',
6515 [
6513 [
6516 (
6514 (
6517 b'A',
6515 b'A',
6518 b'addremove',
6516 b'addremove',
6519 None,
6517 None,
6520 _(b'mark new/missing files as added/removed before shelving'),
6518 _(b'mark new/missing files as added/removed before shelving'),
6521 ),
6519 ),
6522 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6520 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6523 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6521 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6524 (
6522 (
6525 b'',
6523 b'',
6526 b'date',
6524 b'date',
6527 b'',
6525 b'',
6528 _(b'shelve with the specified commit date'),
6526 _(b'shelve with the specified commit date'),
6529 _(b'DATE'),
6527 _(b'DATE'),
6530 ),
6528 ),
6531 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6529 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6532 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6530 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6533 (
6531 (
6534 b'k',
6532 b'k',
6535 b'keep',
6533 b'keep',
6536 False,
6534 False,
6537 _(b'shelve, but keep changes in the working directory'),
6535 _(b'shelve, but keep changes in the working directory'),
6538 ),
6536 ),
6539 (b'l', b'list', None, _(b'list current shelves')),
6537 (b'l', b'list', None, _(b'list current shelves')),
6540 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6538 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6541 (
6539 (
6542 b'n',
6540 b'n',
6543 b'name',
6541 b'name',
6544 b'',
6542 b'',
6545 _(b'use the given name for the shelved commit'),
6543 _(b'use the given name for the shelved commit'),
6546 _(b'NAME'),
6544 _(b'NAME'),
6547 ),
6545 ),
6548 (
6546 (
6549 b'p',
6547 b'p',
6550 b'patch',
6548 b'patch',
6551 None,
6549 None,
6552 _(
6550 _(
6553 b'output patches for changes (provide the names of the shelved '
6551 b'output patches for changes (provide the names of the shelved '
6554 b'changes as positional arguments)'
6552 b'changes as positional arguments)'
6555 ),
6553 ),
6556 ),
6554 ),
6557 (b'i', b'interactive', None, _(b'interactive mode')),
6555 (b'i', b'interactive', None, _(b'interactive mode')),
6558 (
6556 (
6559 b'',
6557 b'',
6560 b'stat',
6558 b'stat',
6561 None,
6559 None,
6562 _(
6560 _(
6563 b'output diffstat-style summary of changes (provide the names of '
6561 b'output diffstat-style summary of changes (provide the names of '
6564 b'the shelved changes as positional arguments)'
6562 b'the shelved changes as positional arguments)'
6565 ),
6563 ),
6566 ),
6564 ),
6567 ]
6565 ]
6568 + cmdutil.walkopts,
6566 + cmdutil.walkopts,
6569 _(b'hg shelve [OPTION]... [FILE]...'),
6567 _(b'hg shelve [OPTION]... [FILE]...'),
6570 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6568 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6571 )
6569 )
6572 def shelve(ui, repo, *pats, **opts):
6570 def shelve(ui, repo, *pats, **opts):
6573 '''save and set aside changes from the working directory
6571 '''save and set aside changes from the working directory
6574
6572
6575 Shelving takes files that "hg status" reports as not clean, saves
6573 Shelving takes files that "hg status" reports as not clean, saves
6576 the modifications to a bundle (a shelved change), and reverts the
6574 the modifications to a bundle (a shelved change), and reverts the
6577 files so that their state in the working directory becomes clean.
6575 files so that their state in the working directory becomes clean.
6578
6576
6579 To restore these changes to the working directory, using "hg
6577 To restore these changes to the working directory, using "hg
6580 unshelve"; this will work even if you switch to a different
6578 unshelve"; this will work even if you switch to a different
6581 commit.
6579 commit.
6582
6580
6583 When no files are specified, "hg shelve" saves all not-clean
6581 When no files are specified, "hg shelve" saves all not-clean
6584 files. If specific files or directories are named, only changes to
6582 files. If specific files or directories are named, only changes to
6585 those files are shelved.
6583 those files are shelved.
6586
6584
6587 In bare shelve (when no files are specified, without interactive,
6585 In bare shelve (when no files are specified, without interactive,
6588 include and exclude option), shelving remembers information if the
6586 include and exclude option), shelving remembers information if the
6589 working directory was on newly created branch, in other words working
6587 working directory was on newly created branch, in other words working
6590 directory was on different branch than its first parent. In this
6588 directory was on different branch than its first parent. In this
6591 situation unshelving restores branch information to the working directory.
6589 situation unshelving restores branch information to the working directory.
6592
6590
6593 Each shelved change has a name that makes it easier to find later.
6591 Each shelved change has a name that makes it easier to find later.
6594 The name of a shelved change defaults to being based on the active
6592 The name of a shelved change defaults to being based on the active
6595 bookmark, or if there is no active bookmark, the current named
6593 bookmark, or if there is no active bookmark, the current named
6596 branch. To specify a different name, use ``--name``.
6594 branch. To specify a different name, use ``--name``.
6597
6595
6598 To see a list of existing shelved changes, use the ``--list``
6596 To see a list of existing shelved changes, use the ``--list``
6599 option. For each shelved change, this will print its name, age,
6597 option. For each shelved change, this will print its name, age,
6600 and description; use ``--patch`` or ``--stat`` for more details.
6598 and description; use ``--patch`` or ``--stat`` for more details.
6601
6599
6602 To delete specific shelved changes, use ``--delete``. To delete
6600 To delete specific shelved changes, use ``--delete``. To delete
6603 all shelved changes, use ``--cleanup``.
6601 all shelved changes, use ``--cleanup``.
6604 '''
6602 '''
6605 opts = pycompat.byteskwargs(opts)
6603 opts = pycompat.byteskwargs(opts)
6606 allowables = [
6604 allowables = [
6607 (b'addremove', {b'create'}), # 'create' is pseudo action
6605 (b'addremove', {b'create'}), # 'create' is pseudo action
6608 (b'unknown', {b'create'}),
6606 (b'unknown', {b'create'}),
6609 (b'cleanup', {b'cleanup'}),
6607 (b'cleanup', {b'cleanup'}),
6610 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6608 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6611 (b'delete', {b'delete'}),
6609 (b'delete', {b'delete'}),
6612 (b'edit', {b'create'}),
6610 (b'edit', {b'create'}),
6613 (b'keep', {b'create'}),
6611 (b'keep', {b'create'}),
6614 (b'list', {b'list'}),
6612 (b'list', {b'list'}),
6615 (b'message', {b'create'}),
6613 (b'message', {b'create'}),
6616 (b'name', {b'create'}),
6614 (b'name', {b'create'}),
6617 (b'patch', {b'patch', b'list'}),
6615 (b'patch', {b'patch', b'list'}),
6618 (b'stat', {b'stat', b'list'}),
6616 (b'stat', {b'stat', b'list'}),
6619 ]
6617 ]
6620
6618
6621 def checkopt(opt):
6619 def checkopt(opt):
6622 if opts.get(opt):
6620 if opts.get(opt):
6623 for i, allowable in allowables:
6621 for i, allowable in allowables:
6624 if opts[i] and opt not in allowable:
6622 if opts[i] and opt not in allowable:
6625 raise error.Abort(
6623 raise error.Abort(
6626 _(
6624 _(
6627 b"options '--%s' and '--%s' may not be "
6625 b"options '--%s' and '--%s' may not be "
6628 b"used together"
6626 b"used together"
6629 )
6627 )
6630 % (opt, i)
6628 % (opt, i)
6631 )
6629 )
6632 return True
6630 return True
6633
6631
6634 if checkopt(b'cleanup'):
6632 if checkopt(b'cleanup'):
6635 if pats:
6633 if pats:
6636 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6634 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6637 return shelvemod.cleanupcmd(ui, repo)
6635 return shelvemod.cleanupcmd(ui, repo)
6638 elif checkopt(b'delete'):
6636 elif checkopt(b'delete'):
6639 return shelvemod.deletecmd(ui, repo, pats)
6637 return shelvemod.deletecmd(ui, repo, pats)
6640 elif checkopt(b'list'):
6638 elif checkopt(b'list'):
6641 return shelvemod.listcmd(ui, repo, pats, opts)
6639 return shelvemod.listcmd(ui, repo, pats, opts)
6642 elif checkopt(b'patch') or checkopt(b'stat'):
6640 elif checkopt(b'patch') or checkopt(b'stat'):
6643 return shelvemod.patchcmds(ui, repo, pats, opts)
6641 return shelvemod.patchcmds(ui, repo, pats, opts)
6644 else:
6642 else:
6645 return shelvemod.createcmd(ui, repo, pats, opts)
6643 return shelvemod.createcmd(ui, repo, pats, opts)
6646
6644
6647
6645
6648 _NOTTERSE = b'nothing'
6646 _NOTTERSE = b'nothing'
6649
6647
6650
6648
6651 @command(
6649 @command(
6652 b'status|st',
6650 b'status|st',
6653 [
6651 [
6654 (b'A', b'all', None, _(b'show status of all files')),
6652 (b'A', b'all', None, _(b'show status of all files')),
6655 (b'm', b'modified', None, _(b'show only modified files')),
6653 (b'm', b'modified', None, _(b'show only modified files')),
6656 (b'a', b'added', None, _(b'show only added files')),
6654 (b'a', b'added', None, _(b'show only added files')),
6657 (b'r', b'removed', None, _(b'show only removed files')),
6655 (b'r', b'removed', None, _(b'show only removed files')),
6658 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6656 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6659 (b'c', b'clean', None, _(b'show only files without changes')),
6657 (b'c', b'clean', None, _(b'show only files without changes')),
6660 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6658 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6661 (b'i', b'ignored', None, _(b'show only ignored files')),
6659 (b'i', b'ignored', None, _(b'show only ignored files')),
6662 (b'n', b'no-status', None, _(b'hide status prefix')),
6660 (b'n', b'no-status', None, _(b'hide status prefix')),
6663 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6661 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6664 (
6662 (
6665 b'C',
6663 b'C',
6666 b'copies',
6664 b'copies',
6667 None,
6665 None,
6668 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6666 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6669 ),
6667 ),
6670 (
6668 (
6671 b'0',
6669 b'0',
6672 b'print0',
6670 b'print0',
6673 None,
6671 None,
6674 _(b'end filenames with NUL, for use with xargs'),
6672 _(b'end filenames with NUL, for use with xargs'),
6675 ),
6673 ),
6676 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6674 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6677 (
6675 (
6678 b'',
6676 b'',
6679 b'change',
6677 b'change',
6680 b'',
6678 b'',
6681 _(b'list the changed files of a revision'),
6679 _(b'list the changed files of a revision'),
6682 _(b'REV'),
6680 _(b'REV'),
6683 ),
6681 ),
6684 ]
6682 ]
6685 + walkopts
6683 + walkopts
6686 + subrepoopts
6684 + subrepoopts
6687 + formatteropts,
6685 + formatteropts,
6688 _(b'[OPTION]... [FILE]...'),
6686 _(b'[OPTION]... [FILE]...'),
6689 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6687 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6690 helpbasic=True,
6688 helpbasic=True,
6691 inferrepo=True,
6689 inferrepo=True,
6692 intents={INTENT_READONLY},
6690 intents={INTENT_READONLY},
6693 )
6691 )
6694 def status(ui, repo, *pats, **opts):
6692 def status(ui, repo, *pats, **opts):
6695 """show changed files in the working directory
6693 """show changed files in the working directory
6696
6694
6697 Show status of files in the repository. If names are given, only
6695 Show status of files in the repository. If names are given, only
6698 files that match are shown. Files that are clean or ignored or
6696 files that match are shown. Files that are clean or ignored or
6699 the source of a copy/move operation, are not listed unless
6697 the source of a copy/move operation, are not listed unless
6700 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6698 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6701 Unless options described with "show only ..." are given, the
6699 Unless options described with "show only ..." are given, the
6702 options -mardu are used.
6700 options -mardu are used.
6703
6701
6704 Option -q/--quiet hides untracked (unknown and ignored) files
6702 Option -q/--quiet hides untracked (unknown and ignored) files
6705 unless explicitly requested with -u/--unknown or -i/--ignored.
6703 unless explicitly requested with -u/--unknown or -i/--ignored.
6706
6704
6707 .. note::
6705 .. note::
6708
6706
6709 :hg:`status` may appear to disagree with diff if permissions have
6707 :hg:`status` may appear to disagree with diff if permissions have
6710 changed or a merge has occurred. The standard diff format does
6708 changed or a merge has occurred. The standard diff format does
6711 not report permission changes and diff only reports changes
6709 not report permission changes and diff only reports changes
6712 relative to one merge parent.
6710 relative to one merge parent.
6713
6711
6714 If one revision is given, it is used as the base revision.
6712 If one revision is given, it is used as the base revision.
6715 If two revisions are given, the differences between them are
6713 If two revisions are given, the differences between them are
6716 shown. The --change option can also be used as a shortcut to list
6714 shown. The --change option can also be used as a shortcut to list
6717 the changed files of a revision from its first parent.
6715 the changed files of a revision from its first parent.
6718
6716
6719 The codes used to show the status of files are::
6717 The codes used to show the status of files are::
6720
6718
6721 M = modified
6719 M = modified
6722 A = added
6720 A = added
6723 R = removed
6721 R = removed
6724 C = clean
6722 C = clean
6725 ! = missing (deleted by non-hg command, but still tracked)
6723 ! = missing (deleted by non-hg command, but still tracked)
6726 ? = not tracked
6724 ? = not tracked
6727 I = ignored
6725 I = ignored
6728 = origin of the previous file (with --copies)
6726 = origin of the previous file (with --copies)
6729
6727
6730 .. container:: verbose
6728 .. container:: verbose
6731
6729
6732 The -t/--terse option abbreviates the output by showing only the directory
6730 The -t/--terse option abbreviates the output by showing only the directory
6733 name if all the files in it share the same status. The option takes an
6731 name if all the files in it share the same status. The option takes an
6734 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6732 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6735 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6733 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6736 for 'ignored' and 'c' for clean.
6734 for 'ignored' and 'c' for clean.
6737
6735
6738 It abbreviates only those statuses which are passed. Note that clean and
6736 It abbreviates only those statuses which are passed. Note that clean and
6739 ignored files are not displayed with '--terse ic' unless the -c/--clean
6737 ignored files are not displayed with '--terse ic' unless the -c/--clean
6740 and -i/--ignored options are also used.
6738 and -i/--ignored options are also used.
6741
6739
6742 The -v/--verbose option shows information when the repository is in an
6740 The -v/--verbose option shows information when the repository is in an
6743 unfinished merge, shelve, rebase state etc. You can have this behavior
6741 unfinished merge, shelve, rebase state etc. You can have this behavior
6744 turned on by default by enabling the ``commands.status.verbose`` option.
6742 turned on by default by enabling the ``commands.status.verbose`` option.
6745
6743
6746 You can skip displaying some of these states by setting
6744 You can skip displaying some of these states by setting
6747 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6745 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6748 'histedit', 'merge', 'rebase', or 'unshelve'.
6746 'histedit', 'merge', 'rebase', or 'unshelve'.
6749
6747
6750 Template:
6748 Template:
6751
6749
6752 The following keywords are supported in addition to the common template
6750 The following keywords are supported in addition to the common template
6753 keywords and functions. See also :hg:`help templates`.
6751 keywords and functions. See also :hg:`help templates`.
6754
6752
6755 :path: String. Repository-absolute path of the file.
6753 :path: String. Repository-absolute path of the file.
6756 :source: String. Repository-absolute path of the file originated from.
6754 :source: String. Repository-absolute path of the file originated from.
6757 Available if ``--copies`` is specified.
6755 Available if ``--copies`` is specified.
6758 :status: String. Character denoting file's status.
6756 :status: String. Character denoting file's status.
6759
6757
6760 Examples:
6758 Examples:
6761
6759
6762 - show changes in the working directory relative to a
6760 - show changes in the working directory relative to a
6763 changeset::
6761 changeset::
6764
6762
6765 hg status --rev 9353
6763 hg status --rev 9353
6766
6764
6767 - show changes in the working directory relative to the
6765 - show changes in the working directory relative to the
6768 current directory (see :hg:`help patterns` for more information)::
6766 current directory (see :hg:`help patterns` for more information)::
6769
6767
6770 hg status re:
6768 hg status re:
6771
6769
6772 - show all changes including copies in an existing changeset::
6770 - show all changes including copies in an existing changeset::
6773
6771
6774 hg status --copies --change 9353
6772 hg status --copies --change 9353
6775
6773
6776 - get a NUL separated list of added files, suitable for xargs::
6774 - get a NUL separated list of added files, suitable for xargs::
6777
6775
6778 hg status -an0
6776 hg status -an0
6779
6777
6780 - show more information about the repository status, abbreviating
6778 - show more information about the repository status, abbreviating
6781 added, removed, modified, deleted, and untracked paths::
6779 added, removed, modified, deleted, and untracked paths::
6782
6780
6783 hg status -v -t mardu
6781 hg status -v -t mardu
6784
6782
6785 Returns 0 on success.
6783 Returns 0 on success.
6786
6784
6787 """
6785 """
6788
6786
6789 opts = pycompat.byteskwargs(opts)
6787 opts = pycompat.byteskwargs(opts)
6790 revs = opts.get(b'rev')
6788 revs = opts.get(b'rev')
6791 change = opts.get(b'change')
6789 change = opts.get(b'change')
6792 terse = opts.get(b'terse')
6790 terse = opts.get(b'terse')
6793 if terse is _NOTTERSE:
6791 if terse is _NOTTERSE:
6794 if revs:
6792 if revs:
6795 terse = b''
6793 terse = b''
6796 else:
6794 else:
6797 terse = ui.config(b'commands', b'status.terse')
6795 terse = ui.config(b'commands', b'status.terse')
6798
6796
6799 if revs and change:
6797 if revs and change:
6800 msg = _(b'cannot specify --rev and --change at the same time')
6798 msg = _(b'cannot specify --rev and --change at the same time')
6801 raise error.Abort(msg)
6799 raise error.Abort(msg)
6802 elif revs and terse:
6800 elif revs and terse:
6803 msg = _(b'cannot use --terse with --rev')
6801 msg = _(b'cannot use --terse with --rev')
6804 raise error.Abort(msg)
6802 raise error.Abort(msg)
6805 elif change:
6803 elif change:
6806 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6804 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6807 ctx2 = scmutil.revsingle(repo, change, None)
6805 ctx2 = scmutil.revsingle(repo, change, None)
6808 ctx1 = ctx2.p1()
6806 ctx1 = ctx2.p1()
6809 else:
6807 else:
6810 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6808 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6811 ctx1, ctx2 = scmutil.revpair(repo, revs)
6809 ctx1, ctx2 = scmutil.revpair(repo, revs)
6812
6810
6813 forcerelativevalue = None
6811 forcerelativevalue = None
6814 if ui.hasconfig(b'commands', b'status.relative'):
6812 if ui.hasconfig(b'commands', b'status.relative'):
6815 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6813 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6816 uipathfn = scmutil.getuipathfn(
6814 uipathfn = scmutil.getuipathfn(
6817 repo,
6815 repo,
6818 legacyrelativevalue=bool(pats),
6816 legacyrelativevalue=bool(pats),
6819 forcerelativevalue=forcerelativevalue,
6817 forcerelativevalue=forcerelativevalue,
6820 )
6818 )
6821
6819
6822 if opts.get(b'print0'):
6820 if opts.get(b'print0'):
6823 end = b'\0'
6821 end = b'\0'
6824 else:
6822 else:
6825 end = b'\n'
6823 end = b'\n'
6826 states = b'modified added removed deleted unknown ignored clean'.split()
6824 states = b'modified added removed deleted unknown ignored clean'.split()
6827 show = [k for k in states if opts.get(k)]
6825 show = [k for k in states if opts.get(k)]
6828 if opts.get(b'all'):
6826 if opts.get(b'all'):
6829 show += ui.quiet and (states[:4] + [b'clean']) or states
6827 show += ui.quiet and (states[:4] + [b'clean']) or states
6830
6828
6831 if not show:
6829 if not show:
6832 if ui.quiet:
6830 if ui.quiet:
6833 show = states[:4]
6831 show = states[:4]
6834 else:
6832 else:
6835 show = states[:5]
6833 show = states[:5]
6836
6834
6837 m = scmutil.match(ctx2, pats, opts)
6835 m = scmutil.match(ctx2, pats, opts)
6838 if terse:
6836 if terse:
6839 # we need to compute clean and unknown to terse
6837 # we need to compute clean and unknown to terse
6840 stat = repo.status(
6838 stat = repo.status(
6841 ctx1.node(),
6839 ctx1.node(),
6842 ctx2.node(),
6840 ctx2.node(),
6843 m,
6841 m,
6844 b'ignored' in show or b'i' in terse,
6842 b'ignored' in show or b'i' in terse,
6845 clean=True,
6843 clean=True,
6846 unknown=True,
6844 unknown=True,
6847 listsubrepos=opts.get(b'subrepos'),
6845 listsubrepos=opts.get(b'subrepos'),
6848 )
6846 )
6849
6847
6850 stat = cmdutil.tersedir(stat, terse)
6848 stat = cmdutil.tersedir(stat, terse)
6851 else:
6849 else:
6852 stat = repo.status(
6850 stat = repo.status(
6853 ctx1.node(),
6851 ctx1.node(),
6854 ctx2.node(),
6852 ctx2.node(),
6855 m,
6853 m,
6856 b'ignored' in show,
6854 b'ignored' in show,
6857 b'clean' in show,
6855 b'clean' in show,
6858 b'unknown' in show,
6856 b'unknown' in show,
6859 opts.get(b'subrepos'),
6857 opts.get(b'subrepos'),
6860 )
6858 )
6861
6859
6862 changestates = zip(
6860 changestates = zip(
6863 states,
6861 states,
6864 pycompat.iterbytestr(b'MAR!?IC'),
6862 pycompat.iterbytestr(b'MAR!?IC'),
6865 [getattr(stat, s.decode('utf8')) for s in states],
6863 [getattr(stat, s.decode('utf8')) for s in states],
6866 )
6864 )
6867
6865
6868 copy = {}
6866 copy = {}
6869 if (
6867 if (
6870 opts.get(b'all')
6868 opts.get(b'all')
6871 or opts.get(b'copies')
6869 or opts.get(b'copies')
6872 or ui.configbool(b'ui', b'statuscopies')
6870 or ui.configbool(b'ui', b'statuscopies')
6873 ) and not opts.get(b'no_status'):
6871 ) and not opts.get(b'no_status'):
6874 copy = copies.pathcopies(ctx1, ctx2, m)
6872 copy = copies.pathcopies(ctx1, ctx2, m)
6875
6873
6876 morestatus = None
6874 morestatus = None
6877 if (
6875 if (
6878 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6876 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6879 ) and not ui.plain():
6877 ) and not ui.plain():
6880 morestatus = cmdutil.readmorestatus(repo)
6878 morestatus = cmdutil.readmorestatus(repo)
6881
6879
6882 ui.pager(b'status')
6880 ui.pager(b'status')
6883 fm = ui.formatter(b'status', opts)
6881 fm = ui.formatter(b'status', opts)
6884 fmt = b'%s' + end
6882 fmt = b'%s' + end
6885 showchar = not opts.get(b'no_status')
6883 showchar = not opts.get(b'no_status')
6886
6884
6887 for state, char, files in changestates:
6885 for state, char, files in changestates:
6888 if state in show:
6886 if state in show:
6889 label = b'status.' + state
6887 label = b'status.' + state
6890 for f in files:
6888 for f in files:
6891 fm.startitem()
6889 fm.startitem()
6892 fm.context(ctx=ctx2)
6890 fm.context(ctx=ctx2)
6893 fm.data(itemtype=b'file', path=f)
6891 fm.data(itemtype=b'file', path=f)
6894 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6892 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6895 fm.plain(fmt % uipathfn(f), label=label)
6893 fm.plain(fmt % uipathfn(f), label=label)
6896 if f in copy:
6894 if f in copy:
6897 fm.data(source=copy[f])
6895 fm.data(source=copy[f])
6898 fm.plain(
6896 fm.plain(
6899 (b' %s' + end) % uipathfn(copy[f]),
6897 (b' %s' + end) % uipathfn(copy[f]),
6900 label=b'status.copied',
6898 label=b'status.copied',
6901 )
6899 )
6902 if morestatus:
6900 if morestatus:
6903 morestatus.formatfile(f, fm)
6901 morestatus.formatfile(f, fm)
6904
6902
6905 if morestatus:
6903 if morestatus:
6906 morestatus.formatfooter(fm)
6904 morestatus.formatfooter(fm)
6907 fm.end()
6905 fm.end()
6908
6906
6909
6907
6910 @command(
6908 @command(
6911 b'summary|sum',
6909 b'summary|sum',
6912 [(b'', b'remote', None, _(b'check for push and pull'))],
6910 [(b'', b'remote', None, _(b'check for push and pull'))],
6913 b'[--remote]',
6911 b'[--remote]',
6914 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6912 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6915 helpbasic=True,
6913 helpbasic=True,
6916 intents={INTENT_READONLY},
6914 intents={INTENT_READONLY},
6917 )
6915 )
6918 def summary(ui, repo, **opts):
6916 def summary(ui, repo, **opts):
6919 """summarize working directory state
6917 """summarize working directory state
6920
6918
6921 This generates a brief summary of the working directory state,
6919 This generates a brief summary of the working directory state,
6922 including parents, branch, commit status, phase and available updates.
6920 including parents, branch, commit status, phase and available updates.
6923
6921
6924 With the --remote option, this will check the default paths for
6922 With the --remote option, this will check the default paths for
6925 incoming and outgoing changes. This can be time-consuming.
6923 incoming and outgoing changes. This can be time-consuming.
6926
6924
6927 Returns 0 on success.
6925 Returns 0 on success.
6928 """
6926 """
6929
6927
6930 opts = pycompat.byteskwargs(opts)
6928 opts = pycompat.byteskwargs(opts)
6931 ui.pager(b'summary')
6929 ui.pager(b'summary')
6932 ctx = repo[None]
6930 ctx = repo[None]
6933 parents = ctx.parents()
6931 parents = ctx.parents()
6934 pnode = parents[0].node()
6932 pnode = parents[0].node()
6935 marks = []
6933 marks = []
6936
6934
6937 try:
6935 try:
6938 ms = mergemod.mergestate.read(repo)
6936 ms = mergemod.mergestate.read(repo)
6939 except error.UnsupportedMergeRecords as e:
6937 except error.UnsupportedMergeRecords as e:
6940 s = b' '.join(e.recordtypes)
6938 s = b' '.join(e.recordtypes)
6941 ui.warn(
6939 ui.warn(
6942 _(b'warning: merge state has unsupported record types: %s\n') % s
6940 _(b'warning: merge state has unsupported record types: %s\n') % s
6943 )
6941 )
6944 unresolved = []
6942 unresolved = []
6945 else:
6943 else:
6946 unresolved = list(ms.unresolved())
6944 unresolved = list(ms.unresolved())
6947
6945
6948 for p in parents:
6946 for p in parents:
6949 # label with log.changeset (instead of log.parent) since this
6947 # label with log.changeset (instead of log.parent) since this
6950 # shows a working directory parent *changeset*:
6948 # shows a working directory parent *changeset*:
6951 # i18n: column positioning for "hg summary"
6949 # i18n: column positioning for "hg summary"
6952 ui.write(
6950 ui.write(
6953 _(b'parent: %d:%s ') % (p.rev(), p),
6951 _(b'parent: %d:%s ') % (p.rev(), p),
6954 label=logcmdutil.changesetlabels(p),
6952 label=logcmdutil.changesetlabels(p),
6955 )
6953 )
6956 ui.write(b' '.join(p.tags()), label=b'log.tag')
6954 ui.write(b' '.join(p.tags()), label=b'log.tag')
6957 if p.bookmarks():
6955 if p.bookmarks():
6958 marks.extend(p.bookmarks())
6956 marks.extend(p.bookmarks())
6959 if p.rev() == -1:
6957 if p.rev() == -1:
6960 if not len(repo):
6958 if not len(repo):
6961 ui.write(_(b' (empty repository)'))
6959 ui.write(_(b' (empty repository)'))
6962 else:
6960 else:
6963 ui.write(_(b' (no revision checked out)'))
6961 ui.write(_(b' (no revision checked out)'))
6964 if p.obsolete():
6962 if p.obsolete():
6965 ui.write(_(b' (obsolete)'))
6963 ui.write(_(b' (obsolete)'))
6966 if p.isunstable():
6964 if p.isunstable():
6967 instabilities = (
6965 instabilities = (
6968 ui.label(instability, b'trouble.%s' % instability)
6966 ui.label(instability, b'trouble.%s' % instability)
6969 for instability in p.instabilities()
6967 for instability in p.instabilities()
6970 )
6968 )
6971 ui.write(b' (' + b', '.join(instabilities) + b')')
6969 ui.write(b' (' + b', '.join(instabilities) + b')')
6972 ui.write(b'\n')
6970 ui.write(b'\n')
6973 if p.description():
6971 if p.description():
6974 ui.status(
6972 ui.status(
6975 b' ' + p.description().splitlines()[0].strip() + b'\n',
6973 b' ' + p.description().splitlines()[0].strip() + b'\n',
6976 label=b'log.summary',
6974 label=b'log.summary',
6977 )
6975 )
6978
6976
6979 branch = ctx.branch()
6977 branch = ctx.branch()
6980 bheads = repo.branchheads(branch)
6978 bheads = repo.branchheads(branch)
6981 # i18n: column positioning for "hg summary"
6979 # i18n: column positioning for "hg summary"
6982 m = _(b'branch: %s\n') % branch
6980 m = _(b'branch: %s\n') % branch
6983 if branch != b'default':
6981 if branch != b'default':
6984 ui.write(m, label=b'log.branch')
6982 ui.write(m, label=b'log.branch')
6985 else:
6983 else:
6986 ui.status(m, label=b'log.branch')
6984 ui.status(m, label=b'log.branch')
6987
6985
6988 if marks:
6986 if marks:
6989 active = repo._activebookmark
6987 active = repo._activebookmark
6990 # i18n: column positioning for "hg summary"
6988 # i18n: column positioning for "hg summary"
6991 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6989 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6992 if active is not None:
6990 if active is not None:
6993 if active in marks:
6991 if active in marks:
6994 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6992 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6995 marks.remove(active)
6993 marks.remove(active)
6996 else:
6994 else:
6997 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6995 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6998 for m in marks:
6996 for m in marks:
6999 ui.write(b' ' + m, label=b'log.bookmark')
6997 ui.write(b' ' + m, label=b'log.bookmark')
7000 ui.write(b'\n', label=b'log.bookmark')
6998 ui.write(b'\n', label=b'log.bookmark')
7001
6999
7002 status = repo.status(unknown=True)
7000 status = repo.status(unknown=True)
7003
7001
7004 c = repo.dirstate.copies()
7002 c = repo.dirstate.copies()
7005 copied, renamed = [], []
7003 copied, renamed = [], []
7006 for d, s in pycompat.iteritems(c):
7004 for d, s in pycompat.iteritems(c):
7007 if s in status.removed:
7005 if s in status.removed:
7008 status.removed.remove(s)
7006 status.removed.remove(s)
7009 renamed.append(d)
7007 renamed.append(d)
7010 else:
7008 else:
7011 copied.append(d)
7009 copied.append(d)
7012 if d in status.added:
7010 if d in status.added:
7013 status.added.remove(d)
7011 status.added.remove(d)
7014
7012
7015 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7013 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7016
7014
7017 labels = [
7015 labels = [
7018 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7016 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7019 (ui.label(_(b'%d added'), b'status.added'), status.added),
7017 (ui.label(_(b'%d added'), b'status.added'), status.added),
7020 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7018 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7021 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7019 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7022 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7020 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7023 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7021 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7024 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7022 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7025 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7023 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7026 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7024 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7027 ]
7025 ]
7028 t = []
7026 t = []
7029 for l, s in labels:
7027 for l, s in labels:
7030 if s:
7028 if s:
7031 t.append(l % len(s))
7029 t.append(l % len(s))
7032
7030
7033 t = b', '.join(t)
7031 t = b', '.join(t)
7034 cleanworkdir = False
7032 cleanworkdir = False
7035
7033
7036 if repo.vfs.exists(b'graftstate'):
7034 if repo.vfs.exists(b'graftstate'):
7037 t += _(b' (graft in progress)')
7035 t += _(b' (graft in progress)')
7038 if repo.vfs.exists(b'updatestate'):
7036 if repo.vfs.exists(b'updatestate'):
7039 t += _(b' (interrupted update)')
7037 t += _(b' (interrupted update)')
7040 elif len(parents) > 1:
7038 elif len(parents) > 1:
7041 t += _(b' (merge)')
7039 t += _(b' (merge)')
7042 elif branch != parents[0].branch():
7040 elif branch != parents[0].branch():
7043 t += _(b' (new branch)')
7041 t += _(b' (new branch)')
7044 elif parents[0].closesbranch() and pnode in repo.branchheads(
7042 elif parents[0].closesbranch() and pnode in repo.branchheads(
7045 branch, closed=True
7043 branch, closed=True
7046 ):
7044 ):
7047 t += _(b' (head closed)')
7045 t += _(b' (head closed)')
7048 elif not (
7046 elif not (
7049 status.modified
7047 status.modified
7050 or status.added
7048 or status.added
7051 or status.removed
7049 or status.removed
7052 or renamed
7050 or renamed
7053 or copied
7051 or copied
7054 or subs
7052 or subs
7055 ):
7053 ):
7056 t += _(b' (clean)')
7054 t += _(b' (clean)')
7057 cleanworkdir = True
7055 cleanworkdir = True
7058 elif pnode not in bheads:
7056 elif pnode not in bheads:
7059 t += _(b' (new branch head)')
7057 t += _(b' (new branch head)')
7060
7058
7061 if parents:
7059 if parents:
7062 pendingphase = max(p.phase() for p in parents)
7060 pendingphase = max(p.phase() for p in parents)
7063 else:
7061 else:
7064 pendingphase = phases.public
7062 pendingphase = phases.public
7065
7063
7066 if pendingphase > phases.newcommitphase(ui):
7064 if pendingphase > phases.newcommitphase(ui):
7067 t += b' (%s)' % phases.phasenames[pendingphase]
7065 t += b' (%s)' % phases.phasenames[pendingphase]
7068
7066
7069 if cleanworkdir:
7067 if cleanworkdir:
7070 # i18n: column positioning for "hg summary"
7068 # i18n: column positioning for "hg summary"
7071 ui.status(_(b'commit: %s\n') % t.strip())
7069 ui.status(_(b'commit: %s\n') % t.strip())
7072 else:
7070 else:
7073 # i18n: column positioning for "hg summary"
7071 # i18n: column positioning for "hg summary"
7074 ui.write(_(b'commit: %s\n') % t.strip())
7072 ui.write(_(b'commit: %s\n') % t.strip())
7075
7073
7076 # all ancestors of branch heads - all ancestors of parent = new csets
7074 # all ancestors of branch heads - all ancestors of parent = new csets
7077 new = len(
7075 new = len(
7078 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7076 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7079 )
7077 )
7080
7078
7081 if new == 0:
7079 if new == 0:
7082 # i18n: column positioning for "hg summary"
7080 # i18n: column positioning for "hg summary"
7083 ui.status(_(b'update: (current)\n'))
7081 ui.status(_(b'update: (current)\n'))
7084 elif pnode not in bheads:
7082 elif pnode not in bheads:
7085 # i18n: column positioning for "hg summary"
7083 # i18n: column positioning for "hg summary"
7086 ui.write(_(b'update: %d new changesets (update)\n') % new)
7084 ui.write(_(b'update: %d new changesets (update)\n') % new)
7087 else:
7085 else:
7088 # i18n: column positioning for "hg summary"
7086 # i18n: column positioning for "hg summary"
7089 ui.write(
7087 ui.write(
7090 _(b'update: %d new changesets, %d branch heads (merge)\n')
7088 _(b'update: %d new changesets, %d branch heads (merge)\n')
7091 % (new, len(bheads))
7089 % (new, len(bheads))
7092 )
7090 )
7093
7091
7094 t = []
7092 t = []
7095 draft = len(repo.revs(b'draft()'))
7093 draft = len(repo.revs(b'draft()'))
7096 if draft:
7094 if draft:
7097 t.append(_(b'%d draft') % draft)
7095 t.append(_(b'%d draft') % draft)
7098 secret = len(repo.revs(b'secret()'))
7096 secret = len(repo.revs(b'secret()'))
7099 if secret:
7097 if secret:
7100 t.append(_(b'%d secret') % secret)
7098 t.append(_(b'%d secret') % secret)
7101
7099
7102 if draft or secret:
7100 if draft or secret:
7103 ui.status(_(b'phases: %s\n') % b', '.join(t))
7101 ui.status(_(b'phases: %s\n') % b', '.join(t))
7104
7102
7105 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7103 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7106 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7104 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7107 numtrouble = len(repo.revs(trouble + b"()"))
7105 numtrouble = len(repo.revs(trouble + b"()"))
7108 # We write all the possibilities to ease translation
7106 # We write all the possibilities to ease translation
7109 troublemsg = {
7107 troublemsg = {
7110 b"orphan": _(b"orphan: %d changesets"),
7108 b"orphan": _(b"orphan: %d changesets"),
7111 b"contentdivergent": _(b"content-divergent: %d changesets"),
7109 b"contentdivergent": _(b"content-divergent: %d changesets"),
7112 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7110 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7113 }
7111 }
7114 if numtrouble > 0:
7112 if numtrouble > 0:
7115 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7113 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7116
7114
7117 cmdutil.summaryhooks(ui, repo)
7115 cmdutil.summaryhooks(ui, repo)
7118
7116
7119 if opts.get(b'remote'):
7117 if opts.get(b'remote'):
7120 needsincoming, needsoutgoing = True, True
7118 needsincoming, needsoutgoing = True, True
7121 else:
7119 else:
7122 needsincoming, needsoutgoing = False, False
7120 needsincoming, needsoutgoing = False, False
7123 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7121 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7124 if i:
7122 if i:
7125 needsincoming = True
7123 needsincoming = True
7126 if o:
7124 if o:
7127 needsoutgoing = True
7125 needsoutgoing = True
7128 if not needsincoming and not needsoutgoing:
7126 if not needsincoming and not needsoutgoing:
7129 return
7127 return
7130
7128
7131 def getincoming():
7129 def getincoming():
7132 source, branches = hg.parseurl(ui.expandpath(b'default'))
7130 source, branches = hg.parseurl(ui.expandpath(b'default'))
7133 sbranch = branches[0]
7131 sbranch = branches[0]
7134 try:
7132 try:
7135 other = hg.peer(repo, {}, source)
7133 other = hg.peer(repo, {}, source)
7136 except error.RepoError:
7134 except error.RepoError:
7137 if opts.get(b'remote'):
7135 if opts.get(b'remote'):
7138 raise
7136 raise
7139 return source, sbranch, None, None, None
7137 return source, sbranch, None, None, None
7140 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7138 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7141 if revs:
7139 if revs:
7142 revs = [other.lookup(rev) for rev in revs]
7140 revs = [other.lookup(rev) for rev in revs]
7143 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7141 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7144 repo.ui.pushbuffer()
7142 repo.ui.pushbuffer()
7145 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7143 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7146 repo.ui.popbuffer()
7144 repo.ui.popbuffer()
7147 return source, sbranch, other, commoninc, commoninc[1]
7145 return source, sbranch, other, commoninc, commoninc[1]
7148
7146
7149 if needsincoming:
7147 if needsincoming:
7150 source, sbranch, sother, commoninc, incoming = getincoming()
7148 source, sbranch, sother, commoninc, incoming = getincoming()
7151 else:
7149 else:
7152 source = sbranch = sother = commoninc = incoming = None
7150 source = sbranch = sother = commoninc = incoming = None
7153
7151
7154 def getoutgoing():
7152 def getoutgoing():
7155 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7153 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7156 dbranch = branches[0]
7154 dbranch = branches[0]
7157 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7155 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7158 if source != dest:
7156 if source != dest:
7159 try:
7157 try:
7160 dother = hg.peer(repo, {}, dest)
7158 dother = hg.peer(repo, {}, dest)
7161 except error.RepoError:
7159 except error.RepoError:
7162 if opts.get(b'remote'):
7160 if opts.get(b'remote'):
7163 raise
7161 raise
7164 return dest, dbranch, None, None
7162 return dest, dbranch, None, None
7165 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7163 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7166 elif sother is None:
7164 elif sother is None:
7167 # there is no explicit destination peer, but source one is invalid
7165 # there is no explicit destination peer, but source one is invalid
7168 return dest, dbranch, None, None
7166 return dest, dbranch, None, None
7169 else:
7167 else:
7170 dother = sother
7168 dother = sother
7171 if source != dest or (sbranch is not None and sbranch != dbranch):
7169 if source != dest or (sbranch is not None and sbranch != dbranch):
7172 common = None
7170 common = None
7173 else:
7171 else:
7174 common = commoninc
7172 common = commoninc
7175 if revs:
7173 if revs:
7176 revs = [repo.lookup(rev) for rev in revs]
7174 revs = [repo.lookup(rev) for rev in revs]
7177 repo.ui.pushbuffer()
7175 repo.ui.pushbuffer()
7178 outgoing = discovery.findcommonoutgoing(
7176 outgoing = discovery.findcommonoutgoing(
7179 repo, dother, onlyheads=revs, commoninc=common
7177 repo, dother, onlyheads=revs, commoninc=common
7180 )
7178 )
7181 repo.ui.popbuffer()
7179 repo.ui.popbuffer()
7182 return dest, dbranch, dother, outgoing
7180 return dest, dbranch, dother, outgoing
7183
7181
7184 if needsoutgoing:
7182 if needsoutgoing:
7185 dest, dbranch, dother, outgoing = getoutgoing()
7183 dest, dbranch, dother, outgoing = getoutgoing()
7186 else:
7184 else:
7187 dest = dbranch = dother = outgoing = None
7185 dest = dbranch = dother = outgoing = None
7188
7186
7189 if opts.get(b'remote'):
7187 if opts.get(b'remote'):
7190 t = []
7188 t = []
7191 if incoming:
7189 if incoming:
7192 t.append(_(b'1 or more incoming'))
7190 t.append(_(b'1 or more incoming'))
7193 o = outgoing.missing
7191 o = outgoing.missing
7194 if o:
7192 if o:
7195 t.append(_(b'%d outgoing') % len(o))
7193 t.append(_(b'%d outgoing') % len(o))
7196 other = dother or sother
7194 other = dother or sother
7197 if b'bookmarks' in other.listkeys(b'namespaces'):
7195 if b'bookmarks' in other.listkeys(b'namespaces'):
7198 counts = bookmarks.summary(repo, other)
7196 counts = bookmarks.summary(repo, other)
7199 if counts[0] > 0:
7197 if counts[0] > 0:
7200 t.append(_(b'%d incoming bookmarks') % counts[0])
7198 t.append(_(b'%d incoming bookmarks') % counts[0])
7201 if counts[1] > 0:
7199 if counts[1] > 0:
7202 t.append(_(b'%d outgoing bookmarks') % counts[1])
7200 t.append(_(b'%d outgoing bookmarks') % counts[1])
7203
7201
7204 if t:
7202 if t:
7205 # i18n: column positioning for "hg summary"
7203 # i18n: column positioning for "hg summary"
7206 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7204 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7207 else:
7205 else:
7208 # i18n: column positioning for "hg summary"
7206 # i18n: column positioning for "hg summary"
7209 ui.status(_(b'remote: (synced)\n'))
7207 ui.status(_(b'remote: (synced)\n'))
7210
7208
7211 cmdutil.summaryremotehooks(
7209 cmdutil.summaryremotehooks(
7212 ui,
7210 ui,
7213 repo,
7211 repo,
7214 opts,
7212 opts,
7215 (
7213 (
7216 (source, sbranch, sother, commoninc),
7214 (source, sbranch, sother, commoninc),
7217 (dest, dbranch, dother, outgoing),
7215 (dest, dbranch, dother, outgoing),
7218 ),
7216 ),
7219 )
7217 )
7220
7218
7221
7219
7222 @command(
7220 @command(
7223 b'tag',
7221 b'tag',
7224 [
7222 [
7225 (b'f', b'force', None, _(b'force tag')),
7223 (b'f', b'force', None, _(b'force tag')),
7226 (b'l', b'local', None, _(b'make the tag local')),
7224 (b'l', b'local', None, _(b'make the tag local')),
7227 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7225 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7228 (b'', b'remove', None, _(b'remove a tag')),
7226 (b'', b'remove', None, _(b'remove a tag')),
7229 # -l/--local is already there, commitopts cannot be used
7227 # -l/--local is already there, commitopts cannot be used
7230 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7228 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7231 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7229 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7232 ]
7230 ]
7233 + commitopts2,
7231 + commitopts2,
7234 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7232 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7235 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7233 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7236 )
7234 )
7237 def tag(ui, repo, name1, *names, **opts):
7235 def tag(ui, repo, name1, *names, **opts):
7238 """add one or more tags for the current or given revision
7236 """add one or more tags for the current or given revision
7239
7237
7240 Name a particular revision using <name>.
7238 Name a particular revision using <name>.
7241
7239
7242 Tags are used to name particular revisions of the repository and are
7240 Tags are used to name particular revisions of the repository and are
7243 very useful to compare different revisions, to go back to significant
7241 very useful to compare different revisions, to go back to significant
7244 earlier versions or to mark branch points as releases, etc. Changing
7242 earlier versions or to mark branch points as releases, etc. Changing
7245 an existing tag is normally disallowed; use -f/--force to override.
7243 an existing tag is normally disallowed; use -f/--force to override.
7246
7244
7247 If no revision is given, the parent of the working directory is
7245 If no revision is given, the parent of the working directory is
7248 used.
7246 used.
7249
7247
7250 To facilitate version control, distribution, and merging of tags,
7248 To facilitate version control, distribution, and merging of tags,
7251 they are stored as a file named ".hgtags" which is managed similarly
7249 they are stored as a file named ".hgtags" which is managed similarly
7252 to other project files and can be hand-edited if necessary. This
7250 to other project files and can be hand-edited if necessary. This
7253 also means that tagging creates a new commit. The file
7251 also means that tagging creates a new commit. The file
7254 ".hg/localtags" is used for local tags (not shared among
7252 ".hg/localtags" is used for local tags (not shared among
7255 repositories).
7253 repositories).
7256
7254
7257 Tag commits are usually made at the head of a branch. If the parent
7255 Tag commits are usually made at the head of a branch. If the parent
7258 of the working directory is not a branch head, :hg:`tag` aborts; use
7256 of the working directory is not a branch head, :hg:`tag` aborts; use
7259 -f/--force to force the tag commit to be based on a non-head
7257 -f/--force to force the tag commit to be based on a non-head
7260 changeset.
7258 changeset.
7261
7259
7262 See :hg:`help dates` for a list of formats valid for -d/--date.
7260 See :hg:`help dates` for a list of formats valid for -d/--date.
7263
7261
7264 Since tag names have priority over branch names during revision
7262 Since tag names have priority over branch names during revision
7265 lookup, using an existing branch name as a tag name is discouraged.
7263 lookup, using an existing branch name as a tag name is discouraged.
7266
7264
7267 Returns 0 on success.
7265 Returns 0 on success.
7268 """
7266 """
7269 opts = pycompat.byteskwargs(opts)
7267 opts = pycompat.byteskwargs(opts)
7270 with repo.wlock(), repo.lock():
7268 with repo.wlock(), repo.lock():
7271 rev_ = b"."
7269 rev_ = b"."
7272 names = [t.strip() for t in (name1,) + names]
7270 names = [t.strip() for t in (name1,) + names]
7273 if len(names) != len(set(names)):
7271 if len(names) != len(set(names)):
7274 raise error.Abort(_(b'tag names must be unique'))
7272 raise error.Abort(_(b'tag names must be unique'))
7275 for n in names:
7273 for n in names:
7276 scmutil.checknewlabel(repo, n, b'tag')
7274 scmutil.checknewlabel(repo, n, b'tag')
7277 if not n:
7275 if not n:
7278 raise error.Abort(
7276 raise error.Abort(
7279 _(b'tag names cannot consist entirely of whitespace')
7277 _(b'tag names cannot consist entirely of whitespace')
7280 )
7278 )
7281 if opts.get(b'rev') and opts.get(b'remove'):
7279 if opts.get(b'rev') and opts.get(b'remove'):
7282 raise error.Abort(_(b"--rev and --remove are incompatible"))
7280 raise error.Abort(_(b"--rev and --remove are incompatible"))
7283 if opts.get(b'rev'):
7281 if opts.get(b'rev'):
7284 rev_ = opts[b'rev']
7282 rev_ = opts[b'rev']
7285 message = opts.get(b'message')
7283 message = opts.get(b'message')
7286 if opts.get(b'remove'):
7284 if opts.get(b'remove'):
7287 if opts.get(b'local'):
7285 if opts.get(b'local'):
7288 expectedtype = b'local'
7286 expectedtype = b'local'
7289 else:
7287 else:
7290 expectedtype = b'global'
7288 expectedtype = b'global'
7291
7289
7292 for n in names:
7290 for n in names:
7293 if repo.tagtype(n) == b'global':
7291 if repo.tagtype(n) == b'global':
7294 alltags = tagsmod.findglobaltags(ui, repo)
7292 alltags = tagsmod.findglobaltags(ui, repo)
7295 if alltags[n][0] == nullid:
7293 if alltags[n][0] == nullid:
7296 raise error.Abort(_(b"tag '%s' is already removed") % n)
7294 raise error.Abort(_(b"tag '%s' is already removed") % n)
7297 if not repo.tagtype(n):
7295 if not repo.tagtype(n):
7298 raise error.Abort(_(b"tag '%s' does not exist") % n)
7296 raise error.Abort(_(b"tag '%s' does not exist") % n)
7299 if repo.tagtype(n) != expectedtype:
7297 if repo.tagtype(n) != expectedtype:
7300 if expectedtype == b'global':
7298 if expectedtype == b'global':
7301 raise error.Abort(
7299 raise error.Abort(
7302 _(b"tag '%s' is not a global tag") % n
7300 _(b"tag '%s' is not a global tag") % n
7303 )
7301 )
7304 else:
7302 else:
7305 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7303 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7306 rev_ = b'null'
7304 rev_ = b'null'
7307 if not message:
7305 if not message:
7308 # we don't translate commit messages
7306 # we don't translate commit messages
7309 message = b'Removed tag %s' % b', '.join(names)
7307 message = b'Removed tag %s' % b', '.join(names)
7310 elif not opts.get(b'force'):
7308 elif not opts.get(b'force'):
7311 for n in names:
7309 for n in names:
7312 if n in repo.tags():
7310 if n in repo.tags():
7313 raise error.Abort(
7311 raise error.Abort(
7314 _(b"tag '%s' already exists (use -f to force)") % n
7312 _(b"tag '%s' already exists (use -f to force)") % n
7315 )
7313 )
7316 if not opts.get(b'local'):
7314 if not opts.get(b'local'):
7317 p1, p2 = repo.dirstate.parents()
7315 p1, p2 = repo.dirstate.parents()
7318 if p2 != nullid:
7316 if p2 != nullid:
7319 raise error.Abort(_(b'uncommitted merge'))
7317 raise error.Abort(_(b'uncommitted merge'))
7320 bheads = repo.branchheads()
7318 bheads = repo.branchheads()
7321 if not opts.get(b'force') and bheads and p1 not in bheads:
7319 if not opts.get(b'force') and bheads and p1 not in bheads:
7322 raise error.Abort(
7320 raise error.Abort(
7323 _(
7321 _(
7324 b'working directory is not at a branch head '
7322 b'working directory is not at a branch head '
7325 b'(use -f to force)'
7323 b'(use -f to force)'
7326 )
7324 )
7327 )
7325 )
7328 node = scmutil.revsingle(repo, rev_).node()
7326 node = scmutil.revsingle(repo, rev_).node()
7329
7327
7330 if not message:
7328 if not message:
7331 # we don't translate commit messages
7329 # we don't translate commit messages
7332 message = b'Added tag %s for changeset %s' % (
7330 message = b'Added tag %s for changeset %s' % (
7333 b', '.join(names),
7331 b', '.join(names),
7334 short(node),
7332 short(node),
7335 )
7333 )
7336
7334
7337 date = opts.get(b'date')
7335 date = opts.get(b'date')
7338 if date:
7336 if date:
7339 date = dateutil.parsedate(date)
7337 date = dateutil.parsedate(date)
7340
7338
7341 if opts.get(b'remove'):
7339 if opts.get(b'remove'):
7342 editform = b'tag.remove'
7340 editform = b'tag.remove'
7343 else:
7341 else:
7344 editform = b'tag.add'
7342 editform = b'tag.add'
7345 editor = cmdutil.getcommiteditor(
7343 editor = cmdutil.getcommiteditor(
7346 editform=editform, **pycompat.strkwargs(opts)
7344 editform=editform, **pycompat.strkwargs(opts)
7347 )
7345 )
7348
7346
7349 # don't allow tagging the null rev
7347 # don't allow tagging the null rev
7350 if (
7348 if (
7351 not opts.get(b'remove')
7349 not opts.get(b'remove')
7352 and scmutil.revsingle(repo, rev_).rev() == nullrev
7350 and scmutil.revsingle(repo, rev_).rev() == nullrev
7353 ):
7351 ):
7354 raise error.Abort(_(b"cannot tag null revision"))
7352 raise error.Abort(_(b"cannot tag null revision"))
7355
7353
7356 tagsmod.tag(
7354 tagsmod.tag(
7357 repo,
7355 repo,
7358 names,
7356 names,
7359 node,
7357 node,
7360 message,
7358 message,
7361 opts.get(b'local'),
7359 opts.get(b'local'),
7362 opts.get(b'user'),
7360 opts.get(b'user'),
7363 date,
7361 date,
7364 editor=editor,
7362 editor=editor,
7365 )
7363 )
7366
7364
7367
7365
7368 @command(
7366 @command(
7369 b'tags',
7367 b'tags',
7370 formatteropts,
7368 formatteropts,
7371 b'',
7369 b'',
7372 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7370 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7373 intents={INTENT_READONLY},
7371 intents={INTENT_READONLY},
7374 )
7372 )
7375 def tags(ui, repo, **opts):
7373 def tags(ui, repo, **opts):
7376 """list repository tags
7374 """list repository tags
7377
7375
7378 This lists both regular and local tags. When the -v/--verbose
7376 This lists both regular and local tags. When the -v/--verbose
7379 switch is used, a third column "local" is printed for local tags.
7377 switch is used, a third column "local" is printed for local tags.
7380 When the -q/--quiet switch is used, only the tag name is printed.
7378 When the -q/--quiet switch is used, only the tag name is printed.
7381
7379
7382 .. container:: verbose
7380 .. container:: verbose
7383
7381
7384 Template:
7382 Template:
7385
7383
7386 The following keywords are supported in addition to the common template
7384 The following keywords are supported in addition to the common template
7387 keywords and functions such as ``{tag}``. See also
7385 keywords and functions such as ``{tag}``. See also
7388 :hg:`help templates`.
7386 :hg:`help templates`.
7389
7387
7390 :type: String. ``local`` for local tags.
7388 :type: String. ``local`` for local tags.
7391
7389
7392 Returns 0 on success.
7390 Returns 0 on success.
7393 """
7391 """
7394
7392
7395 opts = pycompat.byteskwargs(opts)
7393 opts = pycompat.byteskwargs(opts)
7396 ui.pager(b'tags')
7394 ui.pager(b'tags')
7397 fm = ui.formatter(b'tags', opts)
7395 fm = ui.formatter(b'tags', opts)
7398 hexfunc = fm.hexfunc
7396 hexfunc = fm.hexfunc
7399
7397
7400 for t, n in reversed(repo.tagslist()):
7398 for t, n in reversed(repo.tagslist()):
7401 hn = hexfunc(n)
7399 hn = hexfunc(n)
7402 label = b'tags.normal'
7400 label = b'tags.normal'
7403 tagtype = b''
7401 tagtype = b''
7404 if repo.tagtype(t) == b'local':
7402 if repo.tagtype(t) == b'local':
7405 label = b'tags.local'
7403 label = b'tags.local'
7406 tagtype = b'local'
7404 tagtype = b'local'
7407
7405
7408 fm.startitem()
7406 fm.startitem()
7409 fm.context(repo=repo)
7407 fm.context(repo=repo)
7410 fm.write(b'tag', b'%s', t, label=label)
7408 fm.write(b'tag', b'%s', t, label=label)
7411 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7409 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7412 fm.condwrite(
7410 fm.condwrite(
7413 not ui.quiet,
7411 not ui.quiet,
7414 b'rev node',
7412 b'rev node',
7415 fmt,
7413 fmt,
7416 repo.changelog.rev(n),
7414 repo.changelog.rev(n),
7417 hn,
7415 hn,
7418 label=label,
7416 label=label,
7419 )
7417 )
7420 fm.condwrite(
7418 fm.condwrite(
7421 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7419 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7422 )
7420 )
7423 fm.plain(b'\n')
7421 fm.plain(b'\n')
7424 fm.end()
7422 fm.end()
7425
7423
7426
7424
7427 @command(
7425 @command(
7428 b'tip',
7426 b'tip',
7429 [
7427 [
7430 (b'p', b'patch', None, _(b'show patch')),
7428 (b'p', b'patch', None, _(b'show patch')),
7431 (b'g', b'git', None, _(b'use git extended diff format')),
7429 (b'g', b'git', None, _(b'use git extended diff format')),
7432 ]
7430 ]
7433 + templateopts,
7431 + templateopts,
7434 _(b'[-p] [-g]'),
7432 _(b'[-p] [-g]'),
7435 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7433 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7436 )
7434 )
7437 def tip(ui, repo, **opts):
7435 def tip(ui, repo, **opts):
7438 """show the tip revision (DEPRECATED)
7436 """show the tip revision (DEPRECATED)
7439
7437
7440 The tip revision (usually just called the tip) is the changeset
7438 The tip revision (usually just called the tip) is the changeset
7441 most recently added to the repository (and therefore the most
7439 most recently added to the repository (and therefore the most
7442 recently changed head).
7440 recently changed head).
7443
7441
7444 If you have just made a commit, that commit will be the tip. If
7442 If you have just made a commit, that commit will be the tip. If
7445 you have just pulled changes from another repository, the tip of
7443 you have just pulled changes from another repository, the tip of
7446 that repository becomes the current tip. The "tip" tag is special
7444 that repository becomes the current tip. The "tip" tag is special
7447 and cannot be renamed or assigned to a different changeset.
7445 and cannot be renamed or assigned to a different changeset.
7448
7446
7449 This command is deprecated, please use :hg:`heads` instead.
7447 This command is deprecated, please use :hg:`heads` instead.
7450
7448
7451 Returns 0 on success.
7449 Returns 0 on success.
7452 """
7450 """
7453 opts = pycompat.byteskwargs(opts)
7451 opts = pycompat.byteskwargs(opts)
7454 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7452 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7455 displayer.show(repo[b'tip'])
7453 displayer.show(repo[b'tip'])
7456 displayer.close()
7454 displayer.close()
7457
7455
7458
7456
7459 @command(
7457 @command(
7460 b'unbundle',
7458 b'unbundle',
7461 [
7459 [
7462 (
7460 (
7463 b'u',
7461 b'u',
7464 b'update',
7462 b'update',
7465 None,
7463 None,
7466 _(b'update to new branch head if changesets were unbundled'),
7464 _(b'update to new branch head if changesets were unbundled'),
7467 )
7465 )
7468 ],
7466 ],
7469 _(b'[-u] FILE...'),
7467 _(b'[-u] FILE...'),
7470 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7468 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7471 )
7469 )
7472 def unbundle(ui, repo, fname1, *fnames, **opts):
7470 def unbundle(ui, repo, fname1, *fnames, **opts):
7473 """apply one or more bundle files
7471 """apply one or more bundle files
7474
7472
7475 Apply one or more bundle files generated by :hg:`bundle`.
7473 Apply one or more bundle files generated by :hg:`bundle`.
7476
7474
7477 Returns 0 on success, 1 if an update has unresolved files.
7475 Returns 0 on success, 1 if an update has unresolved files.
7478 """
7476 """
7479 fnames = (fname1,) + fnames
7477 fnames = (fname1,) + fnames
7480
7478
7481 with repo.lock():
7479 with repo.lock():
7482 for fname in fnames:
7480 for fname in fnames:
7483 f = hg.openpath(ui, fname)
7481 f = hg.openpath(ui, fname)
7484 gen = exchange.readbundle(ui, f, fname)
7482 gen = exchange.readbundle(ui, f, fname)
7485 if isinstance(gen, streamclone.streamcloneapplier):
7483 if isinstance(gen, streamclone.streamcloneapplier):
7486 raise error.Abort(
7484 raise error.Abort(
7487 _(
7485 _(
7488 b'packed bundles cannot be applied with '
7486 b'packed bundles cannot be applied with '
7489 b'"hg unbundle"'
7487 b'"hg unbundle"'
7490 ),
7488 ),
7491 hint=_(b'use "hg debugapplystreamclonebundle"'),
7489 hint=_(b'use "hg debugapplystreamclonebundle"'),
7492 )
7490 )
7493 url = b'bundle:' + fname
7491 url = b'bundle:' + fname
7494 try:
7492 try:
7495 txnname = b'unbundle'
7493 txnname = b'unbundle'
7496 if not isinstance(gen, bundle2.unbundle20):
7494 if not isinstance(gen, bundle2.unbundle20):
7497 txnname = b'unbundle\n%s' % util.hidepassword(url)
7495 txnname = b'unbundle\n%s' % util.hidepassword(url)
7498 with repo.transaction(txnname) as tr:
7496 with repo.transaction(txnname) as tr:
7499 op = bundle2.applybundle(
7497 op = bundle2.applybundle(
7500 repo, gen, tr, source=b'unbundle', url=url
7498 repo, gen, tr, source=b'unbundle', url=url
7501 )
7499 )
7502 except error.BundleUnknownFeatureError as exc:
7500 except error.BundleUnknownFeatureError as exc:
7503 raise error.Abort(
7501 raise error.Abort(
7504 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7502 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7505 hint=_(
7503 hint=_(
7506 b"see https://mercurial-scm.org/"
7504 b"see https://mercurial-scm.org/"
7507 b"wiki/BundleFeature for more "
7505 b"wiki/BundleFeature for more "
7508 b"information"
7506 b"information"
7509 ),
7507 ),
7510 )
7508 )
7511 modheads = bundle2.combinechangegroupresults(op)
7509 modheads = bundle2.combinechangegroupresults(op)
7512
7510
7513 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7511 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7514
7512
7515
7513
7516 @command(
7514 @command(
7517 b'unshelve',
7515 b'unshelve',
7518 [
7516 [
7519 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7517 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7520 (
7518 (
7521 b'c',
7519 b'c',
7522 b'continue',
7520 b'continue',
7523 None,
7521 None,
7524 _(b'continue an incomplete unshelve operation'),
7522 _(b'continue an incomplete unshelve operation'),
7525 ),
7523 ),
7526 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7524 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7527 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7525 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7528 (
7526 (
7529 b'n',
7527 b'n',
7530 b'name',
7528 b'name',
7531 b'',
7529 b'',
7532 _(b'restore shelved change with given name'),
7530 _(b'restore shelved change with given name'),
7533 _(b'NAME'),
7531 _(b'NAME'),
7534 ),
7532 ),
7535 (b't', b'tool', b'', _(b'specify merge tool')),
7533 (b't', b'tool', b'', _(b'specify merge tool')),
7536 (
7534 (
7537 b'',
7535 b'',
7538 b'date',
7536 b'date',
7539 b'',
7537 b'',
7540 _(b'set date for temporary commits (DEPRECATED)'),
7538 _(b'set date for temporary commits (DEPRECATED)'),
7541 _(b'DATE'),
7539 _(b'DATE'),
7542 ),
7540 ),
7543 ],
7541 ],
7544 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7542 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7545 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7543 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7546 )
7544 )
7547 def unshelve(ui, repo, *shelved, **opts):
7545 def unshelve(ui, repo, *shelved, **opts):
7548 """restore a shelved change to the working directory
7546 """restore a shelved change to the working directory
7549
7547
7550 This command accepts an optional name of a shelved change to
7548 This command accepts an optional name of a shelved change to
7551 restore. If none is given, the most recent shelved change is used.
7549 restore. If none is given, the most recent shelved change is used.
7552
7550
7553 If a shelved change is applied successfully, the bundle that
7551 If a shelved change is applied successfully, the bundle that
7554 contains the shelved changes is moved to a backup location
7552 contains the shelved changes is moved to a backup location
7555 (.hg/shelve-backup).
7553 (.hg/shelve-backup).
7556
7554
7557 Since you can restore a shelved change on top of an arbitrary
7555 Since you can restore a shelved change on top of an arbitrary
7558 commit, it is possible that unshelving will result in a conflict
7556 commit, it is possible that unshelving will result in a conflict
7559 between your changes and the commits you are unshelving onto. If
7557 between your changes and the commits you are unshelving onto. If
7560 this occurs, you must resolve the conflict, then use
7558 this occurs, you must resolve the conflict, then use
7561 ``--continue`` to complete the unshelve operation. (The bundle
7559 ``--continue`` to complete the unshelve operation. (The bundle
7562 will not be moved until you successfully complete the unshelve.)
7560 will not be moved until you successfully complete the unshelve.)
7563
7561
7564 (Alternatively, you can use ``--abort`` to abandon an unshelve
7562 (Alternatively, you can use ``--abort`` to abandon an unshelve
7565 that causes a conflict. This reverts the unshelved changes, and
7563 that causes a conflict. This reverts the unshelved changes, and
7566 leaves the bundle in place.)
7564 leaves the bundle in place.)
7567
7565
7568 If bare shelved change (without interactive, include and exclude
7566 If bare shelved change (without interactive, include and exclude
7569 option) was done on newly created branch it would restore branch
7567 option) was done on newly created branch it would restore branch
7570 information to the working directory.
7568 information to the working directory.
7571
7569
7572 After a successful unshelve, the shelved changes are stored in a
7570 After a successful unshelve, the shelved changes are stored in a
7573 backup directory. Only the N most recent backups are kept. N
7571 backup directory. Only the N most recent backups are kept. N
7574 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7572 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7575 configuration option.
7573 configuration option.
7576
7574
7577 .. container:: verbose
7575 .. container:: verbose
7578
7576
7579 Timestamp in seconds is used to decide order of backups. More
7577 Timestamp in seconds is used to decide order of backups. More
7580 than ``maxbackups`` backups are kept, if same timestamp
7578 than ``maxbackups`` backups are kept, if same timestamp
7581 prevents from deciding exact order of them, for safety.
7579 prevents from deciding exact order of them, for safety.
7582
7580
7583 Selected changes can be unshelved with ``--interactive`` flag.
7581 Selected changes can be unshelved with ``--interactive`` flag.
7584 The working directory is updated with the selected changes, and
7582 The working directory is updated with the selected changes, and
7585 only the unselected changes remain shelved.
7583 only the unselected changes remain shelved.
7586 Note: The whole shelve is applied to working directory first before
7584 Note: The whole shelve is applied to working directory first before
7587 running interactively. So, this will bring up all the conflicts between
7585 running interactively. So, this will bring up all the conflicts between
7588 working directory and the shelve, irrespective of which changes will be
7586 working directory and the shelve, irrespective of which changes will be
7589 unshelved.
7587 unshelved.
7590 """
7588 """
7591 with repo.wlock():
7589 with repo.wlock():
7592 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7590 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7593
7591
7594
7592
7595 statemod.addunfinished(
7593 statemod.addunfinished(
7596 b'unshelve',
7594 b'unshelve',
7597 fname=b'shelvedstate',
7595 fname=b'shelvedstate',
7598 continueflag=True,
7596 continueflag=True,
7599 abortfunc=shelvemod.hgabortunshelve,
7597 abortfunc=shelvemod.hgabortunshelve,
7600 continuefunc=shelvemod.hgcontinueunshelve,
7598 continuefunc=shelvemod.hgcontinueunshelve,
7601 cmdmsg=_(b'unshelve already in progress'),
7599 cmdmsg=_(b'unshelve already in progress'),
7602 )
7600 )
7603
7601
7604
7602
7605 @command(
7603 @command(
7606 b'update|up|checkout|co',
7604 b'update|up|checkout|co',
7607 [
7605 [
7608 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7606 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7609 (b'c', b'check', None, _(b'require clean working directory')),
7607 (b'c', b'check', None, _(b'require clean working directory')),
7610 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7608 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7611 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7609 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7612 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7610 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7613 ]
7611 ]
7614 + mergetoolopts,
7612 + mergetoolopts,
7615 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7613 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7616 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7614 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7617 helpbasic=True,
7615 helpbasic=True,
7618 )
7616 )
7619 def update(ui, repo, node=None, **opts):
7617 def update(ui, repo, node=None, **opts):
7620 """update working directory (or switch revisions)
7618 """update working directory (or switch revisions)
7621
7619
7622 Update the repository's working directory to the specified
7620 Update the repository's working directory to the specified
7623 changeset. If no changeset is specified, update to the tip of the
7621 changeset. If no changeset is specified, update to the tip of the
7624 current named branch and move the active bookmark (see :hg:`help
7622 current named branch and move the active bookmark (see :hg:`help
7625 bookmarks`).
7623 bookmarks`).
7626
7624
7627 Update sets the working directory's parent revision to the specified
7625 Update sets the working directory's parent revision to the specified
7628 changeset (see :hg:`help parents`).
7626 changeset (see :hg:`help parents`).
7629
7627
7630 If the changeset is not a descendant or ancestor of the working
7628 If the changeset is not a descendant or ancestor of the working
7631 directory's parent and there are uncommitted changes, the update is
7629 directory's parent and there are uncommitted changes, the update is
7632 aborted. With the -c/--check option, the working directory is checked
7630 aborted. With the -c/--check option, the working directory is checked
7633 for uncommitted changes; if none are found, the working directory is
7631 for uncommitted changes; if none are found, the working directory is
7634 updated to the specified changeset.
7632 updated to the specified changeset.
7635
7633
7636 .. container:: verbose
7634 .. container:: verbose
7637
7635
7638 The -C/--clean, -c/--check, and -m/--merge options control what
7636 The -C/--clean, -c/--check, and -m/--merge options control what
7639 happens if the working directory contains uncommitted changes.
7637 happens if the working directory contains uncommitted changes.
7640 At most of one of them can be specified.
7638 At most of one of them can be specified.
7641
7639
7642 1. If no option is specified, and if
7640 1. If no option is specified, and if
7643 the requested changeset is an ancestor or descendant of
7641 the requested changeset is an ancestor or descendant of
7644 the working directory's parent, the uncommitted changes
7642 the working directory's parent, the uncommitted changes
7645 are merged into the requested changeset and the merged
7643 are merged into the requested changeset and the merged
7646 result is left uncommitted. If the requested changeset is
7644 result is left uncommitted. If the requested changeset is
7647 not an ancestor or descendant (that is, it is on another
7645 not an ancestor or descendant (that is, it is on another
7648 branch), the update is aborted and the uncommitted changes
7646 branch), the update is aborted and the uncommitted changes
7649 are preserved.
7647 are preserved.
7650
7648
7651 2. With the -m/--merge option, the update is allowed even if the
7649 2. With the -m/--merge option, the update is allowed even if the
7652 requested changeset is not an ancestor or descendant of
7650 requested changeset is not an ancestor or descendant of
7653 the working directory's parent.
7651 the working directory's parent.
7654
7652
7655 3. With the -c/--check option, the update is aborted and the
7653 3. With the -c/--check option, the update is aborted and the
7656 uncommitted changes are preserved.
7654 uncommitted changes are preserved.
7657
7655
7658 4. With the -C/--clean option, uncommitted changes are discarded and
7656 4. With the -C/--clean option, uncommitted changes are discarded and
7659 the working directory is updated to the requested changeset.
7657 the working directory is updated to the requested changeset.
7660
7658
7661 To cancel an uncommitted merge (and lose your changes), use
7659 To cancel an uncommitted merge (and lose your changes), use
7662 :hg:`merge --abort`.
7660 :hg:`merge --abort`.
7663
7661
7664 Use null as the changeset to remove the working directory (like
7662 Use null as the changeset to remove the working directory (like
7665 :hg:`clone -U`).
7663 :hg:`clone -U`).
7666
7664
7667 If you want to revert just one file to an older revision, use
7665 If you want to revert just one file to an older revision, use
7668 :hg:`revert [-r REV] NAME`.
7666 :hg:`revert [-r REV] NAME`.
7669
7667
7670 See :hg:`help dates` for a list of formats valid for -d/--date.
7668 See :hg:`help dates` for a list of formats valid for -d/--date.
7671
7669
7672 Returns 0 on success, 1 if there are unresolved files.
7670 Returns 0 on success, 1 if there are unresolved files.
7673 """
7671 """
7674 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7672 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7675 rev = opts.get('rev')
7673 rev = opts.get('rev')
7676 date = opts.get('date')
7674 date = opts.get('date')
7677 clean = opts.get('clean')
7675 clean = opts.get('clean')
7678 check = opts.get('check')
7676 check = opts.get('check')
7679 merge = opts.get('merge')
7677 merge = opts.get('merge')
7680 if rev and node:
7678 if rev and node:
7681 raise error.Abort(_(b"please specify just one revision"))
7679 raise error.Abort(_(b"please specify just one revision"))
7682
7680
7683 if ui.configbool(b'commands', b'update.requiredest'):
7681 if ui.configbool(b'commands', b'update.requiredest'):
7684 if not node and not rev and not date:
7682 if not node and not rev and not date:
7685 raise error.Abort(
7683 raise error.Abort(
7686 _(b'you must specify a destination'),
7684 _(b'you must specify a destination'),
7687 hint=_(b'for example: hg update ".::"'),
7685 hint=_(b'for example: hg update ".::"'),
7688 )
7686 )
7689
7687
7690 if rev is None or rev == b'':
7688 if rev is None or rev == b'':
7691 rev = node
7689 rev = node
7692
7690
7693 if date and rev is not None:
7691 if date and rev is not None:
7694 raise error.Abort(_(b"you can't specify a revision and a date"))
7692 raise error.Abort(_(b"you can't specify a revision and a date"))
7695
7693
7696 updatecheck = None
7694 updatecheck = None
7697 if check:
7695 if check:
7698 updatecheck = b'abort'
7696 updatecheck = b'abort'
7699 elif merge:
7697 elif merge:
7700 updatecheck = b'none'
7698 updatecheck = b'none'
7701
7699
7702 with repo.wlock():
7700 with repo.wlock():
7703 cmdutil.clearunfinished(repo)
7701 cmdutil.clearunfinished(repo)
7704 if date:
7702 if date:
7705 rev = cmdutil.finddate(ui, repo, date)
7703 rev = cmdutil.finddate(ui, repo, date)
7706
7704
7707 # if we defined a bookmark, we have to remember the original name
7705 # if we defined a bookmark, we have to remember the original name
7708 brev = rev
7706 brev = rev
7709 if rev:
7707 if rev:
7710 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7708 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7711 ctx = scmutil.revsingle(repo, rev, default=None)
7709 ctx = scmutil.revsingle(repo, rev, default=None)
7712 rev = ctx.rev()
7710 rev = ctx.rev()
7713 hidden = ctx.hidden()
7711 hidden = ctx.hidden()
7714 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7712 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7715 with ui.configoverride(overrides, b'update'):
7713 with ui.configoverride(overrides, b'update'):
7716 ret = hg.updatetotally(
7714 ret = hg.updatetotally(
7717 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7715 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7718 )
7716 )
7719 if hidden:
7717 if hidden:
7720 ctxstr = ctx.hex()[:12]
7718 ctxstr = ctx.hex()[:12]
7721 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7719 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7722
7720
7723 if ctx.obsolete():
7721 if ctx.obsolete():
7724 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7722 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7725 ui.warn(b"(%s)\n" % obsfatemsg)
7723 ui.warn(b"(%s)\n" % obsfatemsg)
7726 return ret
7724 return ret
7727
7725
7728
7726
7729 @command(
7727 @command(
7730 b'verify',
7728 b'verify',
7731 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7729 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7732 helpcategory=command.CATEGORY_MAINTENANCE,
7730 helpcategory=command.CATEGORY_MAINTENANCE,
7733 )
7731 )
7734 def verify(ui, repo, **opts):
7732 def verify(ui, repo, **opts):
7735 """verify the integrity of the repository
7733 """verify the integrity of the repository
7736
7734
7737 Verify the integrity of the current repository.
7735 Verify the integrity of the current repository.
7738
7736
7739 This will perform an extensive check of the repository's
7737 This will perform an extensive check of the repository's
7740 integrity, validating the hashes and checksums of each entry in
7738 integrity, validating the hashes and checksums of each entry in
7741 the changelog, manifest, and tracked files, as well as the
7739 the changelog, manifest, and tracked files, as well as the
7742 integrity of their crosslinks and indices.
7740 integrity of their crosslinks and indices.
7743
7741
7744 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7742 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7745 for more information about recovery from corruption of the
7743 for more information about recovery from corruption of the
7746 repository.
7744 repository.
7747
7745
7748 Returns 0 on success, 1 if errors are encountered.
7746 Returns 0 on success, 1 if errors are encountered.
7749 """
7747 """
7750 opts = pycompat.byteskwargs(opts)
7748 opts = pycompat.byteskwargs(opts)
7751
7749
7752 level = None
7750 level = None
7753 if opts[b'full']:
7751 if opts[b'full']:
7754 level = verifymod.VERIFY_FULL
7752 level = verifymod.VERIFY_FULL
7755 return hg.verify(repo, level)
7753 return hg.verify(repo, level)
7756
7754
7757
7755
7758 @command(
7756 @command(
7759 b'version',
7757 b'version',
7760 [] + formatteropts,
7758 [] + formatteropts,
7761 helpcategory=command.CATEGORY_HELP,
7759 helpcategory=command.CATEGORY_HELP,
7762 norepo=True,
7760 norepo=True,
7763 intents={INTENT_READONLY},
7761 intents={INTENT_READONLY},
7764 )
7762 )
7765 def version_(ui, **opts):
7763 def version_(ui, **opts):
7766 """output version and copyright information
7764 """output version and copyright information
7767
7765
7768 .. container:: verbose
7766 .. container:: verbose
7769
7767
7770 Template:
7768 Template:
7771
7769
7772 The following keywords are supported. See also :hg:`help templates`.
7770 The following keywords are supported. See also :hg:`help templates`.
7773
7771
7774 :extensions: List of extensions.
7772 :extensions: List of extensions.
7775 :ver: String. Version number.
7773 :ver: String. Version number.
7776
7774
7777 And each entry of ``{extensions}`` provides the following sub-keywords
7775 And each entry of ``{extensions}`` provides the following sub-keywords
7778 in addition to ``{ver}``.
7776 in addition to ``{ver}``.
7779
7777
7780 :bundled: Boolean. True if included in the release.
7778 :bundled: Boolean. True if included in the release.
7781 :name: String. Extension name.
7779 :name: String. Extension name.
7782 """
7780 """
7783 opts = pycompat.byteskwargs(opts)
7781 opts = pycompat.byteskwargs(opts)
7784 if ui.verbose:
7782 if ui.verbose:
7785 ui.pager(b'version')
7783 ui.pager(b'version')
7786 fm = ui.formatter(b"version", opts)
7784 fm = ui.formatter(b"version", opts)
7787 fm.startitem()
7785 fm.startitem()
7788 fm.write(
7786 fm.write(
7789 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7787 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7790 )
7788 )
7791 license = _(
7789 license = _(
7792 b"(see https://mercurial-scm.org for more information)\n"
7790 b"(see https://mercurial-scm.org for more information)\n"
7793 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7791 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7794 b"This is free software; see the source for copying conditions. "
7792 b"This is free software; see the source for copying conditions. "
7795 b"There is NO\nwarranty; "
7793 b"There is NO\nwarranty; "
7796 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7794 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7797 )
7795 )
7798 if not ui.quiet:
7796 if not ui.quiet:
7799 fm.plain(license)
7797 fm.plain(license)
7800
7798
7801 if ui.verbose:
7799 if ui.verbose:
7802 fm.plain(_(b"\nEnabled extensions:\n\n"))
7800 fm.plain(_(b"\nEnabled extensions:\n\n"))
7803 # format names and versions into columns
7801 # format names and versions into columns
7804 names = []
7802 names = []
7805 vers = []
7803 vers = []
7806 isinternals = []
7804 isinternals = []
7807 for name, module in extensions.extensions():
7805 for name, module in extensions.extensions():
7808 names.append(name)
7806 names.append(name)
7809 vers.append(extensions.moduleversion(module) or None)
7807 vers.append(extensions.moduleversion(module) or None)
7810 isinternals.append(extensions.ismoduleinternal(module))
7808 isinternals.append(extensions.ismoduleinternal(module))
7811 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7809 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7812 if names:
7810 if names:
7813 namefmt = b" %%-%ds " % max(len(n) for n in names)
7811 namefmt = b" %%-%ds " % max(len(n) for n in names)
7814 places = [_(b"external"), _(b"internal")]
7812 places = [_(b"external"), _(b"internal")]
7815 for n, v, p in zip(names, vers, isinternals):
7813 for n, v, p in zip(names, vers, isinternals):
7816 fn.startitem()
7814 fn.startitem()
7817 fn.condwrite(ui.verbose, b"name", namefmt, n)
7815 fn.condwrite(ui.verbose, b"name", namefmt, n)
7818 if ui.verbose:
7816 if ui.verbose:
7819 fn.plain(b"%s " % places[p])
7817 fn.plain(b"%s " % places[p])
7820 fn.data(bundled=p)
7818 fn.data(bundled=p)
7821 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7819 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7822 if ui.verbose:
7820 if ui.verbose:
7823 fn.plain(b"\n")
7821 fn.plain(b"\n")
7824 fn.end()
7822 fn.end()
7825 fm.end()
7823 fm.end()
7826
7824
7827
7825
7828 def loadcmdtable(ui, name, cmdtable):
7826 def loadcmdtable(ui, name, cmdtable):
7829 """Load command functions from specified cmdtable
7827 """Load command functions from specified cmdtable
7830 """
7828 """
7831 overrides = [cmd for cmd in cmdtable if cmd in table]
7829 overrides = [cmd for cmd in cmdtable if cmd in table]
7832 if overrides:
7830 if overrides:
7833 ui.warn(
7831 ui.warn(
7834 _(b"extension '%s' overrides commands: %s\n")
7832 _(b"extension '%s' overrides commands: %s\n")
7835 % (name, b" ".join(overrides))
7833 % (name, b" ".join(overrides))
7836 )
7834 )
7837 table.update(cmdtable)
7835 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now