##// END OF EJS Templates
archive: add XZ support if built with Python 3
David Demelier -
r43210:c04e0836 default
parent child Browse files
Show More
@@ -1,350 +1,355 b''
1 # archival.py - revision archival for mercurial
1 # archival.py - revision archival 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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import gzip
10 import gzip
11 import os
11 import os
12 import struct
12 import struct
13 import tarfile
13 import tarfile
14 import time
14 import time
15 import zipfile
15 import zipfile
16 import zlib
16 import zlib
17
17
18 from .i18n import _
18 from .i18n import _
19 from .node import (
19 from .node import (
20 nullrev,
20 nullrev,
21 )
21 )
22
22
23 from . import (
23 from . import (
24 error,
24 error,
25 formatter,
25 formatter,
26 match as matchmod,
26 match as matchmod,
27 pycompat,
27 pycompat,
28 scmutil,
28 scmutil,
29 util,
29 util,
30 vfs as vfsmod,
30 vfs as vfsmod,
31 )
31 )
32 stringio = util.stringio
32 stringio = util.stringio
33
33
34 # from unzip source code:
34 # from unzip source code:
35 _UNX_IFREG = 0x8000
35 _UNX_IFREG = 0x8000
36 _UNX_IFLNK = 0xa000
36 _UNX_IFLNK = 0xa000
37
37
38 def tidyprefix(dest, kind, prefix):
38 def tidyprefix(dest, kind, prefix):
39 '''choose prefix to use for names in archive. make sure prefix is
39 '''choose prefix to use for names in archive. make sure prefix is
40 safe for consumers.'''
40 safe for consumers.'''
41
41
42 if prefix:
42 if prefix:
43 prefix = util.normpath(prefix)
43 prefix = util.normpath(prefix)
44 else:
44 else:
45 if not isinstance(dest, bytes):
45 if not isinstance(dest, bytes):
46 raise ValueError('dest must be string if no prefix')
46 raise ValueError('dest must be string if no prefix')
47 prefix = os.path.basename(dest)
47 prefix = os.path.basename(dest)
48 lower = prefix.lower()
48 lower = prefix.lower()
49 for sfx in exts.get(kind, []):
49 for sfx in exts.get(kind, []):
50 if lower.endswith(sfx):
50 if lower.endswith(sfx):
51 prefix = prefix[:-len(sfx)]
51 prefix = prefix[:-len(sfx)]
52 break
52 break
53 lpfx = os.path.normpath(util.localpath(prefix))
53 lpfx = os.path.normpath(util.localpath(prefix))
54 prefix = util.pconvert(lpfx)
54 prefix = util.pconvert(lpfx)
55 if not prefix.endswith('/'):
55 if not prefix.endswith('/'):
56 prefix += '/'
56 prefix += '/'
57 # Drop the leading '.' path component if present, so Windows can read the
57 # Drop the leading '.' path component if present, so Windows can read the
58 # zip files (issue4634)
58 # zip files (issue4634)
59 if prefix.startswith('./'):
59 if prefix.startswith('./'):
60 prefix = prefix[2:]
60 prefix = prefix[2:]
61 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
61 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
62 raise error.Abort(_('archive prefix contains illegal components'))
62 raise error.Abort(_('archive prefix contains illegal components'))
63 return prefix
63 return prefix
64
64
65 exts = {
65 exts = {
66 'tar': ['.tar'],
66 'tar': ['.tar'],
67 'tbz2': ['.tbz2', '.tar.bz2'],
67 'tbz2': ['.tbz2', '.tar.bz2'],
68 'tgz': ['.tgz', '.tar.gz'],
68 'tgz': ['.tgz', '.tar.gz'],
69 'zip': ['.zip'],
69 'zip': ['.zip'],
70 'txz': ['.txz', '.tar.xz']
70 }
71 }
71
72
72 def guesskind(dest):
73 def guesskind(dest):
73 for kind, extensions in exts.iteritems():
74 for kind, extensions in exts.iteritems():
74 if any(dest.endswith(ext) for ext in extensions):
75 if any(dest.endswith(ext) for ext in extensions):
75 return kind
76 return kind
76 return None
77 return None
77
78
78 def _rootctx(repo):
79 def _rootctx(repo):
79 # repo[0] may be hidden
80 # repo[0] may be hidden
80 for rev in repo:
81 for rev in repo:
81 return repo[rev]
82 return repo[rev]
82 return repo[nullrev]
83 return repo[nullrev]
83
84
84 # {tags} on ctx includes local tags and 'tip', with no current way to limit
85 # {tags} on ctx includes local tags and 'tip', with no current way to limit
85 # that to global tags. Therefore, use {latesttag} as a substitute when
86 # that to global tags. Therefore, use {latesttag} as a substitute when
86 # the distance is 0, since that will be the list of global tags on ctx.
87 # the distance is 0, since that will be the list of global tags on ctx.
87 _defaultmetatemplate = br'''
88 _defaultmetatemplate = br'''
88 repo: {root}
89 repo: {root}
89 node: {ifcontains(rev, revset("wdir()"), "{p1node}{dirty}", "{node}")}
90 node: {ifcontains(rev, revset("wdir()"), "{p1node}{dirty}", "{node}")}
90 branch: {branch|utf8}
91 branch: {branch|utf8}
91 {ifeq(latesttagdistance, 0, join(latesttag % "tag: {tag}", "\n"),
92 {ifeq(latesttagdistance, 0, join(latesttag % "tag: {tag}", "\n"),
92 separate("\n",
93 separate("\n",
93 join(latesttag % "latesttag: {tag}", "\n"),
94 join(latesttag % "latesttag: {tag}", "\n"),
94 "latesttagdistance: {latesttagdistance}",
95 "latesttagdistance: {latesttagdistance}",
95 "changessincelatesttag: {changessincelatesttag}"))}
96 "changessincelatesttag: {changessincelatesttag}"))}
96 '''[1:] # drop leading '\n'
97 '''[1:] # drop leading '\n'
97
98
98 def buildmetadata(ctx):
99 def buildmetadata(ctx):
99 '''build content of .hg_archival.txt'''
100 '''build content of .hg_archival.txt'''
100 repo = ctx.repo()
101 repo = ctx.repo()
101
102
102 opts = {
103 opts = {
103 'template': repo.ui.config('experimental', 'archivemetatemplate',
104 'template': repo.ui.config('experimental', 'archivemetatemplate',
104 _defaultmetatemplate)
105 _defaultmetatemplate)
105 }
106 }
106
107
107 out = util.stringio()
108 out = util.stringio()
108
109
109 fm = formatter.formatter(repo.ui, out, 'archive', opts)
110 fm = formatter.formatter(repo.ui, out, 'archive', opts)
110 fm.startitem()
111 fm.startitem()
111 fm.context(ctx=ctx)
112 fm.context(ctx=ctx)
112 fm.data(root=_rootctx(repo).hex())
113 fm.data(root=_rootctx(repo).hex())
113
114
114 if ctx.rev() is None:
115 if ctx.rev() is None:
115 dirty = ''
116 dirty = ''
116 if ctx.dirty(missing=True):
117 if ctx.dirty(missing=True):
117 dirty = '+'
118 dirty = '+'
118 fm.data(dirty=dirty)
119 fm.data(dirty=dirty)
119 fm.end()
120 fm.end()
120
121
121 return out.getvalue()
122 return out.getvalue()
122
123
123 class tarit(object):
124 class tarit(object):
124 '''write archive to tar file or stream. can write uncompressed,
125 '''write archive to tar file or stream. can write uncompressed,
125 or compress with gzip or bzip2.'''
126 or compress with gzip or bzip2.'''
126
127
127 class GzipFileWithTime(gzip.GzipFile):
128 class GzipFileWithTime(gzip.GzipFile):
128
129
129 def __init__(self, *args, **kw):
130 def __init__(self, *args, **kw):
130 timestamp = None
131 timestamp = None
131 if r'timestamp' in kw:
132 if r'timestamp' in kw:
132 timestamp = kw.pop(r'timestamp')
133 timestamp = kw.pop(r'timestamp')
133 if timestamp is None:
134 if timestamp is None:
134 self.timestamp = time.time()
135 self.timestamp = time.time()
135 else:
136 else:
136 self.timestamp = timestamp
137 self.timestamp = timestamp
137 gzip.GzipFile.__init__(self, *args, **kw)
138 gzip.GzipFile.__init__(self, *args, **kw)
138
139
139 def _write_gzip_header(self):
140 def _write_gzip_header(self):
140 self.fileobj.write('\037\213') # magic header
141 self.fileobj.write('\037\213') # magic header
141 self.fileobj.write('\010') # compression method
142 self.fileobj.write('\010') # compression method
142 fname = self.name
143 fname = self.name
143 if fname and fname.endswith('.gz'):
144 if fname and fname.endswith('.gz'):
144 fname = fname[:-3]
145 fname = fname[:-3]
145 flags = 0
146 flags = 0
146 if fname:
147 if fname:
147 flags = gzip.FNAME
148 flags = gzip.FNAME
148 self.fileobj.write(pycompat.bytechr(flags))
149 self.fileobj.write(pycompat.bytechr(flags))
149 gzip.write32u(self.fileobj, int(self.timestamp))
150 gzip.write32u(self.fileobj, int(self.timestamp))
150 self.fileobj.write('\002')
151 self.fileobj.write('\002')
151 self.fileobj.write('\377')
152 self.fileobj.write('\377')
152 if fname:
153 if fname:
153 self.fileobj.write(fname + '\000')
154 self.fileobj.write(fname + '\000')
154
155
155 def __init__(self, dest, mtime, kind=''):
156 def __init__(self, dest, mtime, kind=''):
156 self.mtime = mtime
157 self.mtime = mtime
157 self.fileobj = None
158 self.fileobj = None
158
159
159 def taropen(mode, name='', fileobj=None):
160 def taropen(mode, name='', fileobj=None):
160 if kind == 'gz':
161 if kind == 'gz':
161 mode = mode[0:1]
162 mode = mode[0:1]
162 if not fileobj:
163 if not fileobj:
163 fileobj = open(name, mode + 'b')
164 fileobj = open(name, mode + 'b')
164 gzfileobj = self.GzipFileWithTime(name,
165 gzfileobj = self.GzipFileWithTime(name,
165 pycompat.sysstr(mode + 'b'),
166 pycompat.sysstr(mode + 'b'),
166 zlib.Z_BEST_COMPRESSION,
167 zlib.Z_BEST_COMPRESSION,
167 fileobj, timestamp=mtime)
168 fileobj, timestamp=mtime)
168 self.fileobj = gzfileobj
169 self.fileobj = gzfileobj
169 return tarfile.TarFile.taropen(
170 return tarfile.TarFile.taropen(
170 name, pycompat.sysstr(mode), gzfileobj)
171 name, pycompat.sysstr(mode), gzfileobj)
171 else:
172 else:
172 return tarfile.open(
173 return tarfile.open(
173 name, pycompat.sysstr(mode + kind), fileobj)
174 name, pycompat.sysstr(mode + kind), fileobj)
174
175
175 if isinstance(dest, bytes):
176 if isinstance(dest, bytes):
176 self.z = taropen('w:', name=dest)
177 self.z = taropen('w:', name=dest)
177 else:
178 else:
178 self.z = taropen('w|', fileobj=dest)
179 self.z = taropen('w|', fileobj=dest)
179
180
180 def addfile(self, name, mode, islink, data):
181 def addfile(self, name, mode, islink, data):
181 name = pycompat.fsdecode(name)
182 name = pycompat.fsdecode(name)
182 i = tarfile.TarInfo(name)
183 i = tarfile.TarInfo(name)
183 i.mtime = self.mtime
184 i.mtime = self.mtime
184 i.size = len(data)
185 i.size = len(data)
185 if islink:
186 if islink:
186 i.type = tarfile.SYMTYPE
187 i.type = tarfile.SYMTYPE
187 i.mode = 0o777
188 i.mode = 0o777
188 i.linkname = pycompat.fsdecode(data)
189 i.linkname = pycompat.fsdecode(data)
189 data = None
190 data = None
190 i.size = 0
191 i.size = 0
191 else:
192 else:
192 i.mode = mode
193 i.mode = mode
193 data = stringio(data)
194 data = stringio(data)
194 self.z.addfile(i, data)
195 self.z.addfile(i, data)
195
196
196 def done(self):
197 def done(self):
197 self.z.close()
198 self.z.close()
198 if self.fileobj:
199 if self.fileobj:
199 self.fileobj.close()
200 self.fileobj.close()
200
201
201 class zipit(object):
202 class zipit(object):
202 '''write archive to zip file or stream. can write uncompressed,
203 '''write archive to zip file or stream. can write uncompressed,
203 or compressed with deflate.'''
204 or compressed with deflate.'''
204
205
205 def __init__(self, dest, mtime, compress=True):
206 def __init__(self, dest, mtime, compress=True):
206 if isinstance(dest, bytes):
207 if isinstance(dest, bytes):
207 dest = pycompat.fsdecode(dest)
208 dest = pycompat.fsdecode(dest)
208 self.z = zipfile.ZipFile(dest, r'w',
209 self.z = zipfile.ZipFile(dest, r'w',
209 compress and zipfile.ZIP_DEFLATED or
210 compress and zipfile.ZIP_DEFLATED or
210 zipfile.ZIP_STORED)
211 zipfile.ZIP_STORED)
211
212
212 # Python's zipfile module emits deprecation warnings if we try
213 # Python's zipfile module emits deprecation warnings if we try
213 # to store files with a date before 1980.
214 # to store files with a date before 1980.
214 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
215 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
215 if mtime < epoch:
216 if mtime < epoch:
216 mtime = epoch
217 mtime = epoch
217
218
218 self.mtime = mtime
219 self.mtime = mtime
219 self.date_time = time.gmtime(mtime)[:6]
220 self.date_time = time.gmtime(mtime)[:6]
220
221
221 def addfile(self, name, mode, islink, data):
222 def addfile(self, name, mode, islink, data):
222 i = zipfile.ZipInfo(pycompat.fsdecode(name), self.date_time)
223 i = zipfile.ZipInfo(pycompat.fsdecode(name), self.date_time)
223 i.compress_type = self.z.compression
224 i.compress_type = self.z.compression
224 # unzip will not honor unix file modes unless file creator is
225 # unzip will not honor unix file modes unless file creator is
225 # set to unix (id 3).
226 # set to unix (id 3).
226 i.create_system = 3
227 i.create_system = 3
227 ftype = _UNX_IFREG
228 ftype = _UNX_IFREG
228 if islink:
229 if islink:
229 mode = 0o777
230 mode = 0o777
230 ftype = _UNX_IFLNK
231 ftype = _UNX_IFLNK
231 i.external_attr = (mode | ftype) << 16
232 i.external_attr = (mode | ftype) << 16
232 # add "extended-timestamp" extra block, because zip archives
233 # add "extended-timestamp" extra block, because zip archives
233 # without this will be extracted with unexpected timestamp,
234 # without this will be extracted with unexpected timestamp,
234 # if TZ is not configured as GMT
235 # if TZ is not configured as GMT
235 i.extra += struct.pack('<hhBl',
236 i.extra += struct.pack('<hhBl',
236 0x5455, # block type: "extended-timestamp"
237 0x5455, # block type: "extended-timestamp"
237 1 + 4, # size of this block
238 1 + 4, # size of this block
238 1, # "modification time is present"
239 1, # "modification time is present"
239 int(self.mtime)) # last modification (UTC)
240 int(self.mtime)) # last modification (UTC)
240 self.z.writestr(i, data)
241 self.z.writestr(i, data)
241
242
242 def done(self):
243 def done(self):
243 self.z.close()
244 self.z.close()
244
245
245 class fileit(object):
246 class fileit(object):
246 '''write archive as files in directory.'''
247 '''write archive as files in directory.'''
247
248
248 def __init__(self, name, mtime):
249 def __init__(self, name, mtime):
249 self.basedir = name
250 self.basedir = name
250 self.opener = vfsmod.vfs(self.basedir)
251 self.opener = vfsmod.vfs(self.basedir)
251 self.mtime = mtime
252 self.mtime = mtime
252
253
253 def addfile(self, name, mode, islink, data):
254 def addfile(self, name, mode, islink, data):
254 if islink:
255 if islink:
255 self.opener.symlink(data, name)
256 self.opener.symlink(data, name)
256 return
257 return
257 f = self.opener(name, "w", atomictemp=False)
258 f = self.opener(name, "w", atomictemp=False)
258 f.write(data)
259 f.write(data)
259 f.close()
260 f.close()
260 destfile = os.path.join(self.basedir, name)
261 destfile = os.path.join(self.basedir, name)
261 os.chmod(destfile, mode)
262 os.chmod(destfile, mode)
262 if self.mtime is not None:
263 if self.mtime is not None:
263 os.utime(destfile, (self.mtime, self.mtime))
264 os.utime(destfile, (self.mtime, self.mtime))
264
265
265 def done(self):
266 def done(self):
266 pass
267 pass
267
268
268 archivers = {
269 archivers = {
269 'files': fileit,
270 'files': fileit,
270 'tar': tarit,
271 'tar': tarit,
271 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
272 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
272 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
273 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
274 'txz': lambda name, mtime: tarit(name, mtime, 'xz'),
273 'uzip': lambda name, mtime: zipit(name, mtime, False),
275 'uzip': lambda name, mtime: zipit(name, mtime, False),
274 'zip': zipit,
276 'zip': zipit,
275 }
277 }
276
278
277 def archive(repo, dest, node, kind, decode=True, match=None,
279 def archive(repo, dest, node, kind, decode=True, match=None,
278 prefix='', mtime=None, subrepos=False):
280 prefix='', mtime=None, subrepos=False):
279 '''create archive of repo as it was at node.
281 '''create archive of repo as it was at node.
280
282
281 dest can be name of directory, name of archive file, or file
283 dest can be name of directory, name of archive file, or file
282 object to write archive to.
284 object to write archive to.
283
285
284 kind is type of archive to create.
286 kind is type of archive to create.
285
287
286 decode tells whether to put files through decode filters from
288 decode tells whether to put files through decode filters from
287 hgrc.
289 hgrc.
288
290
289 match is a matcher to filter names of files to write to archive.
291 match is a matcher to filter names of files to write to archive.
290
292
291 prefix is name of path to put before every archive member.
293 prefix is name of path to put before every archive member.
292
294
293 mtime is the modified time, in seconds, or None to use the changeset time.
295 mtime is the modified time, in seconds, or None to use the changeset time.
294
296
295 subrepos tells whether to include subrepos.
297 subrepos tells whether to include subrepos.
296 '''
298 '''
297
299
300 if kind == 'txz' and not pycompat.ispy3:
301 raise error.Abort(_('xz compression is only available in Python 3'))
302
298 if kind == 'files':
303 if kind == 'files':
299 if prefix:
304 if prefix:
300 raise error.Abort(_('cannot give prefix when archiving to files'))
305 raise error.Abort(_('cannot give prefix when archiving to files'))
301 else:
306 else:
302 prefix = tidyprefix(dest, kind, prefix)
307 prefix = tidyprefix(dest, kind, prefix)
303
308
304 def write(name, mode, islink, getdata):
309 def write(name, mode, islink, getdata):
305 data = getdata()
310 data = getdata()
306 if decode:
311 if decode:
307 data = repo.wwritedata(name, data)
312 data = repo.wwritedata(name, data)
308 archiver.addfile(prefix + name, mode, islink, data)
313 archiver.addfile(prefix + name, mode, islink, data)
309
314
310 if kind not in archivers:
315 if kind not in archivers:
311 raise error.Abort(_("unknown archive type '%s'") % kind)
316 raise error.Abort(_("unknown archive type '%s'") % kind)
312
317
313 ctx = repo[node]
318 ctx = repo[node]
314 archiver = archivers[kind](dest, mtime or ctx.date()[0])
319 archiver = archivers[kind](dest, mtime or ctx.date()[0])
315
320
316 if not match:
321 if not match:
317 match = scmutil.matchall(repo)
322 match = scmutil.matchall(repo)
318
323
319 if repo.ui.configbool("ui", "archivemeta"):
324 if repo.ui.configbool("ui", "archivemeta"):
320 name = '.hg_archival.txt'
325 name = '.hg_archival.txt'
321 if match(name):
326 if match(name):
322 write(name, 0o644, False, lambda: buildmetadata(ctx))
327 write(name, 0o644, False, lambda: buildmetadata(ctx))
323
328
324 files = [f for f in ctx.manifest().matches(match)]
329 files = [f for f in ctx.manifest().matches(match)]
325 total = len(files)
330 total = len(files)
326 if total:
331 if total:
327 files.sort()
332 files.sort()
328 scmutil.prefetchfiles(repo, [ctx.rev()],
333 scmutil.prefetchfiles(repo, [ctx.rev()],
329 scmutil.matchfiles(repo, files))
334 scmutil.matchfiles(repo, files))
330 progress = repo.ui.makeprogress(_('archiving'), unit=_('files'),
335 progress = repo.ui.makeprogress(_('archiving'), unit=_('files'),
331 total=total)
336 total=total)
332 progress.update(0)
337 progress.update(0)
333 for f in files:
338 for f in files:
334 ff = ctx.flags(f)
339 ff = ctx.flags(f)
335 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, ctx[f].data)
340 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, ctx[f].data)
336 progress.increment(item=f)
341 progress.increment(item=f)
337 progress.complete()
342 progress.complete()
338
343
339 if subrepos:
344 if subrepos:
340 for subpath in sorted(ctx.substate):
345 for subpath in sorted(ctx.substate):
341 sub = ctx.workingsub(subpath)
346 sub = ctx.workingsub(subpath)
342 submatch = matchmod.subdirmatcher(subpath, match)
347 submatch = matchmod.subdirmatcher(subpath, match)
343 subprefix = prefix + subpath + '/'
348 subprefix = prefix + subpath + '/'
344 total += sub.archive(archiver, subprefix, submatch, decode)
349 total += sub.archive(archiver, subprefix, submatch, decode)
345
350
346 if total == 0:
351 if total == 0:
347 raise error.Abort(_('no files match the archive pattern'))
352 raise error.Abort(_('no files match the archive pattern'))
348
353
349 archiver.done()
354 archiver.done()
350 return total
355 return total
@@ -1,6451 +1,6452 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 . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 changegroup,
29 changegroup,
30 cmdutil,
30 cmdutil,
31 copies,
31 copies,
32 debugcommands as debugcommandsmod,
32 debugcommands as debugcommandsmod,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 discovery,
35 discovery,
36 encoding,
36 encoding,
37 error,
37 error,
38 exchange,
38 exchange,
39 extensions,
39 extensions,
40 filemerge,
40 filemerge,
41 formatter,
41 formatter,
42 graphmod,
42 graphmod,
43 hbisect,
43 hbisect,
44 help,
44 help,
45 hg,
45 hg,
46 logcmdutil,
46 logcmdutil,
47 merge as mergemod,
47 merge as mergemod,
48 narrowspec,
48 narrowspec,
49 obsolete,
49 obsolete,
50 obsutil,
50 obsutil,
51 patch,
51 patch,
52 phases,
52 phases,
53 pycompat,
53 pycompat,
54 rcutil,
54 rcutil,
55 registrar,
55 registrar,
56 revsetlang,
56 revsetlang,
57 rewriteutil,
57 rewriteutil,
58 scmutil,
58 scmutil,
59 server,
59 server,
60 shelve as shelvemod,
60 shelve as shelvemod,
61 state as statemod,
61 state as statemod,
62 streamclone,
62 streamclone,
63 tags as tagsmod,
63 tags as tagsmod,
64 ui as uimod,
64 ui as uimod,
65 util,
65 util,
66 verify as verifymod,
66 verify as verifymod,
67 wireprotoserver,
67 wireprotoserver,
68 )
68 )
69 from .utils import (
69 from .utils import (
70 dateutil,
70 dateutil,
71 stringutil,
71 stringutil,
72 )
72 )
73
73
74 table = {}
74 table = {}
75 table.update(debugcommandsmod.command._table)
75 table.update(debugcommandsmod.command._table)
76
76
77 command = registrar.command(table)
77 command = registrar.command(table)
78 INTENT_READONLY = registrar.INTENT_READONLY
78 INTENT_READONLY = registrar.INTENT_READONLY
79
79
80 # common command options
80 # common command options
81
81
82 globalopts = [
82 globalopts = [
83 ('R', 'repository', '',
83 ('R', 'repository', '',
84 _('repository root directory or name of overlay bundle file'),
84 _('repository root directory or name of overlay bundle file'),
85 _('REPO')),
85 _('REPO')),
86 ('', 'cwd', '',
86 ('', 'cwd', '',
87 _('change working directory'), _('DIR')),
87 _('change working directory'), _('DIR')),
88 ('y', 'noninteractive', None,
88 ('y', 'noninteractive', None,
89 _('do not prompt, automatically pick the first choice for all prompts')),
89 _('do not prompt, automatically pick the first choice for all prompts')),
90 ('q', 'quiet', None, _('suppress output')),
90 ('q', 'quiet', None, _('suppress output')),
91 ('v', 'verbose', None, _('enable additional output')),
91 ('v', 'verbose', None, _('enable additional output')),
92 ('', 'color', '',
92 ('', 'color', '',
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
94 # and should not be translated
94 # and should not be translated
95 _("when to colorize (boolean, always, auto, never, or debug)"),
95 _("when to colorize (boolean, always, auto, never, or debug)"),
96 _('TYPE')),
96 _('TYPE')),
97 ('', 'config', [],
97 ('', 'config', [],
98 _('set/override config option (use \'section.name=value\')'),
98 _('set/override config option (use \'section.name=value\')'),
99 _('CONFIG')),
99 _('CONFIG')),
100 ('', 'debug', None, _('enable debugging output')),
100 ('', 'debug', None, _('enable debugging output')),
101 ('', 'debugger', None, _('start debugger')),
101 ('', 'debugger', None, _('start debugger')),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
103 _('ENCODE')),
103 _('ENCODE')),
104 ('', 'encodingmode', encoding.encodingmode,
104 ('', 'encodingmode', encoding.encodingmode,
105 _('set the charset encoding mode'), _('MODE')),
105 _('set the charset encoding mode'), _('MODE')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
107 ('', 'time', None, _('time how long the command takes')),
107 ('', 'time', None, _('time how long the command takes')),
108 ('', 'profile', None, _('print command execution profile')),
108 ('', 'profile', None, _('print command execution profile')),
109 ('', 'version', None, _('output version information and exit')),
109 ('', 'version', None, _('output version information and exit')),
110 ('h', 'help', None, _('display help and exit')),
110 ('h', 'help', None, _('display help and exit')),
111 ('', 'hidden', False, _('consider hidden changesets')),
111 ('', 'hidden', False, _('consider hidden changesets')),
112 ('', 'pager', 'auto',
112 ('', 'pager', 'auto',
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
114 ]
114 ]
115
115
116 dryrunopts = cmdutil.dryrunopts
116 dryrunopts = cmdutil.dryrunopts
117 remoteopts = cmdutil.remoteopts
117 remoteopts = cmdutil.remoteopts
118 walkopts = cmdutil.walkopts
118 walkopts = cmdutil.walkopts
119 commitopts = cmdutil.commitopts
119 commitopts = cmdutil.commitopts
120 commitopts2 = cmdutil.commitopts2
120 commitopts2 = cmdutil.commitopts2
121 commitopts3 = cmdutil.commitopts3
121 commitopts3 = cmdutil.commitopts3
122 formatteropts = cmdutil.formatteropts
122 formatteropts = cmdutil.formatteropts
123 templateopts = cmdutil.templateopts
123 templateopts = cmdutil.templateopts
124 logopts = cmdutil.logopts
124 logopts = cmdutil.logopts
125 diffopts = cmdutil.diffopts
125 diffopts = cmdutil.diffopts
126 diffwsopts = cmdutil.diffwsopts
126 diffwsopts = cmdutil.diffwsopts
127 diffopts2 = cmdutil.diffopts2
127 diffopts2 = cmdutil.diffopts2
128 mergetoolopts = cmdutil.mergetoolopts
128 mergetoolopts = cmdutil.mergetoolopts
129 similarityopts = cmdutil.similarityopts
129 similarityopts = cmdutil.similarityopts
130 subrepoopts = cmdutil.subrepoopts
130 subrepoopts = cmdutil.subrepoopts
131 debugrevlogopts = cmdutil.debugrevlogopts
131 debugrevlogopts = cmdutil.debugrevlogopts
132
132
133 # Commands start here, listed alphabetically
133 # Commands start here, listed alphabetically
134
134
135 @command('abort',
135 @command('abort',
136 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
136 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
137 helpbasic=True)
137 helpbasic=True)
138 def abort(ui, repo, **opts):
138 def abort(ui, repo, **opts):
139 """abort an unfinished operation (EXPERIMENTAL)
139 """abort an unfinished operation (EXPERIMENTAL)
140
140
141 Aborts a multistep operation like graft, histedit, rebase, merge,
141 Aborts a multistep operation like graft, histedit, rebase, merge,
142 and unshelve if they are in an unfinished state.
142 and unshelve if they are in an unfinished state.
143
143
144 use --dry-run/-n to dry run the command.
144 use --dry-run/-n to dry run the command.
145 """
145 """
146 dryrun = opts.get(r'dry_run')
146 dryrun = opts.get(r'dry_run')
147 abortstate = cmdutil.getunfinishedstate(repo)
147 abortstate = cmdutil.getunfinishedstate(repo)
148 if not abortstate:
148 if not abortstate:
149 raise error.Abort(_('no operation in progress'))
149 raise error.Abort(_('no operation in progress'))
150 if not abortstate.abortfunc:
150 if not abortstate.abortfunc:
151 raise error.Abort((_("%s in progress but does not support 'hg abort'") %
151 raise error.Abort((_("%s in progress but does not support 'hg abort'") %
152 (abortstate._opname)), hint=abortstate.hint())
152 (abortstate._opname)), hint=abortstate.hint())
153 if dryrun:
153 if dryrun:
154 ui.status(_('%s in progress, will be aborted\n') % (abortstate._opname))
154 ui.status(_('%s in progress, will be aborted\n') % (abortstate._opname))
155 return
155 return
156 return abortstate.abortfunc(ui, repo)
156 return abortstate.abortfunc(ui, repo)
157
157
158 @command('add',
158 @command('add',
159 walkopts + subrepoopts + dryrunopts,
159 walkopts + subrepoopts + dryrunopts,
160 _('[OPTION]... [FILE]...'),
160 _('[OPTION]... [FILE]...'),
161 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
161 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
162 helpbasic=True, inferrepo=True)
162 helpbasic=True, inferrepo=True)
163 def add(ui, repo, *pats, **opts):
163 def add(ui, repo, *pats, **opts):
164 """add the specified files on the next commit
164 """add the specified files on the next commit
165
165
166 Schedule files to be version controlled and added to the
166 Schedule files to be version controlled and added to the
167 repository.
167 repository.
168
168
169 The files will be added to the repository at the next commit. To
169 The files will be added to the repository at the next commit. To
170 undo an add before that, see :hg:`forget`.
170 undo an add before that, see :hg:`forget`.
171
171
172 If no names are given, add all files to the repository (except
172 If no names are given, add all files to the repository (except
173 files matching ``.hgignore``).
173 files matching ``.hgignore``).
174
174
175 .. container:: verbose
175 .. container:: verbose
176
176
177 Examples:
177 Examples:
178
178
179 - New (unknown) files are added
179 - New (unknown) files are added
180 automatically by :hg:`add`::
180 automatically by :hg:`add`::
181
181
182 $ ls
182 $ ls
183 foo.c
183 foo.c
184 $ hg status
184 $ hg status
185 ? foo.c
185 ? foo.c
186 $ hg add
186 $ hg add
187 adding foo.c
187 adding foo.c
188 $ hg status
188 $ hg status
189 A foo.c
189 A foo.c
190
190
191 - Specific files to be added can be specified::
191 - Specific files to be added can be specified::
192
192
193 $ ls
193 $ ls
194 bar.c foo.c
194 bar.c foo.c
195 $ hg status
195 $ hg status
196 ? bar.c
196 ? bar.c
197 ? foo.c
197 ? foo.c
198 $ hg add bar.c
198 $ hg add bar.c
199 $ hg status
199 $ hg status
200 A bar.c
200 A bar.c
201 ? foo.c
201 ? foo.c
202
202
203 Returns 0 if all files are successfully added.
203 Returns 0 if all files are successfully added.
204 """
204 """
205
205
206 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
206 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
207 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
207 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
208 rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
208 rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
209 return rejected and 1 or 0
209 return rejected and 1 or 0
210
210
211 @command('addremove',
211 @command('addremove',
212 similarityopts + subrepoopts + walkopts + dryrunopts,
212 similarityopts + subrepoopts + walkopts + dryrunopts,
213 _('[OPTION]... [FILE]...'),
213 _('[OPTION]... [FILE]...'),
214 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
214 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
215 inferrepo=True)
215 inferrepo=True)
216 def addremove(ui, repo, *pats, **opts):
216 def addremove(ui, repo, *pats, **opts):
217 """add all new files, delete all missing files
217 """add all new files, delete all missing files
218
218
219 Add all new files and remove all missing files from the
219 Add all new files and remove all missing files from the
220 repository.
220 repository.
221
221
222 Unless names are given, new files are ignored if they match any of
222 Unless names are given, new files are ignored if they match any of
223 the patterns in ``.hgignore``. As with add, these changes take
223 the patterns in ``.hgignore``. As with add, these changes take
224 effect at the next commit.
224 effect at the next commit.
225
225
226 Use the -s/--similarity option to detect renamed files. This
226 Use the -s/--similarity option to detect renamed files. This
227 option takes a percentage between 0 (disabled) and 100 (files must
227 option takes a percentage between 0 (disabled) and 100 (files must
228 be identical) as its parameter. With a parameter greater than 0,
228 be identical) as its parameter. With a parameter greater than 0,
229 this compares every removed file with every added file and records
229 this compares every removed file with every added file and records
230 those similar enough as renames. Detecting renamed files this way
230 those similar enough as renames. Detecting renamed files this way
231 can be expensive. After using this option, :hg:`status -C` can be
231 can be expensive. After using this option, :hg:`status -C` can be
232 used to check which files were identified as moved or renamed. If
232 used to check which files were identified as moved or renamed. If
233 not specified, -s/--similarity defaults to 100 and only renames of
233 not specified, -s/--similarity defaults to 100 and only renames of
234 identical files are detected.
234 identical files are detected.
235
235
236 .. container:: verbose
236 .. container:: verbose
237
237
238 Examples:
238 Examples:
239
239
240 - A number of files (bar.c and foo.c) are new,
240 - A number of files (bar.c and foo.c) are new,
241 while foobar.c has been removed (without using :hg:`remove`)
241 while foobar.c has been removed (without using :hg:`remove`)
242 from the repository::
242 from the repository::
243
243
244 $ ls
244 $ ls
245 bar.c foo.c
245 bar.c foo.c
246 $ hg status
246 $ hg status
247 ! foobar.c
247 ! foobar.c
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg addremove
250 $ hg addremove
251 adding bar.c
251 adding bar.c
252 adding foo.c
252 adding foo.c
253 removing foobar.c
253 removing foobar.c
254 $ hg status
254 $ hg status
255 A bar.c
255 A bar.c
256 A foo.c
256 A foo.c
257 R foobar.c
257 R foobar.c
258
258
259 - A file foobar.c was moved to foo.c without using :hg:`rename`.
259 - A file foobar.c was moved to foo.c without using :hg:`rename`.
260 Afterwards, it was edited slightly::
260 Afterwards, it was edited slightly::
261
261
262 $ ls
262 $ ls
263 foo.c
263 foo.c
264 $ hg status
264 $ hg status
265 ! foobar.c
265 ! foobar.c
266 ? foo.c
266 ? foo.c
267 $ hg addremove --similarity 90
267 $ hg addremove --similarity 90
268 removing foobar.c
268 removing foobar.c
269 adding foo.c
269 adding foo.c
270 recording removal of foobar.c as rename to foo.c (94% similar)
270 recording removal of foobar.c as rename to foo.c (94% similar)
271 $ hg status -C
271 $ hg status -C
272 A foo.c
272 A foo.c
273 foobar.c
273 foobar.c
274 R foobar.c
274 R foobar.c
275
275
276 Returns 0 if all files are successfully added.
276 Returns 0 if all files are successfully added.
277 """
277 """
278 opts = pycompat.byteskwargs(opts)
278 opts = pycompat.byteskwargs(opts)
279 if not opts.get('similarity'):
279 if not opts.get('similarity'):
280 opts['similarity'] = '100'
280 opts['similarity'] = '100'
281 matcher = scmutil.match(repo[None], pats, opts)
281 matcher = scmutil.match(repo[None], pats, opts)
282 relative = scmutil.anypats(pats, opts)
282 relative = scmutil.anypats(pats, opts)
283 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
283 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
284 return scmutil.addremove(repo, matcher, "", uipathfn, opts)
284 return scmutil.addremove(repo, matcher, "", uipathfn, opts)
285
285
286 @command('annotate|blame',
286 @command('annotate|blame',
287 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
287 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
288 ('', 'follow', None,
288 ('', 'follow', None,
289 _('follow copies/renames and list the filename (DEPRECATED)')),
289 _('follow copies/renames and list the filename (DEPRECATED)')),
290 ('', 'no-follow', None, _("don't follow copies and renames")),
290 ('', 'no-follow', None, _("don't follow copies and renames")),
291 ('a', 'text', None, _('treat all files as text')),
291 ('a', 'text', None, _('treat all files as text')),
292 ('u', 'user', None, _('list the author (long with -v)')),
292 ('u', 'user', None, _('list the author (long with -v)')),
293 ('f', 'file', None, _('list the filename')),
293 ('f', 'file', None, _('list the filename')),
294 ('d', 'date', None, _('list the date (short with -q)')),
294 ('d', 'date', None, _('list the date (short with -q)')),
295 ('n', 'number', None, _('list the revision number (default)')),
295 ('n', 'number', None, _('list the revision number (default)')),
296 ('c', 'changeset', None, _('list the changeset')),
296 ('c', 'changeset', None, _('list the changeset')),
297 ('l', 'line-number', None, _('show line number at the first appearance')),
297 ('l', 'line-number', None, _('show line number at the first appearance')),
298 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
298 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
299 ] + diffwsopts + walkopts + formatteropts,
299 ] + diffwsopts + walkopts + formatteropts,
300 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
300 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
301 helpcategory=command.CATEGORY_FILE_CONTENTS,
301 helpcategory=command.CATEGORY_FILE_CONTENTS,
302 helpbasic=True, inferrepo=True)
302 helpbasic=True, inferrepo=True)
303 def annotate(ui, repo, *pats, **opts):
303 def annotate(ui, repo, *pats, **opts):
304 """show changeset information by line for each file
304 """show changeset information by line for each file
305
305
306 List changes in files, showing the revision id responsible for
306 List changes in files, showing the revision id responsible for
307 each line.
307 each line.
308
308
309 This command is useful for discovering when a change was made and
309 This command is useful for discovering when a change was made and
310 by whom.
310 by whom.
311
311
312 If you include --file, --user, or --date, the revision number is
312 If you include --file, --user, or --date, the revision number is
313 suppressed unless you also include --number.
313 suppressed unless you also include --number.
314
314
315 Without the -a/--text option, annotate will avoid processing files
315 Without the -a/--text option, annotate will avoid processing files
316 it detects as binary. With -a, annotate will annotate the file
316 it detects as binary. With -a, annotate will annotate the file
317 anyway, although the results will probably be neither useful
317 anyway, although the results will probably be neither useful
318 nor desirable.
318 nor desirable.
319
319
320 .. container:: verbose
320 .. container:: verbose
321
321
322 Template:
322 Template:
323
323
324 The following keywords are supported in addition to the common template
324 The following keywords are supported in addition to the common template
325 keywords and functions. See also :hg:`help templates`.
325 keywords and functions. See also :hg:`help templates`.
326
326
327 :lines: List of lines with annotation data.
327 :lines: List of lines with annotation data.
328 :path: String. Repository-absolute path of the specified file.
328 :path: String. Repository-absolute path of the specified file.
329
329
330 And each entry of ``{lines}`` provides the following sub-keywords in
330 And each entry of ``{lines}`` provides the following sub-keywords in
331 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
331 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
332
332
333 :line: String. Line content.
333 :line: String. Line content.
334 :lineno: Integer. Line number at that revision.
334 :lineno: Integer. Line number at that revision.
335 :path: String. Repository-absolute path of the file at that revision.
335 :path: String. Repository-absolute path of the file at that revision.
336
336
337 See :hg:`help templates.operators` for the list expansion syntax.
337 See :hg:`help templates.operators` for the list expansion syntax.
338
338
339 Returns 0 on success.
339 Returns 0 on success.
340 """
340 """
341 opts = pycompat.byteskwargs(opts)
341 opts = pycompat.byteskwargs(opts)
342 if not pats:
342 if not pats:
343 raise error.Abort(_('at least one filename or pattern is required'))
343 raise error.Abort(_('at least one filename or pattern is required'))
344
344
345 if opts.get('follow'):
345 if opts.get('follow'):
346 # --follow is deprecated and now just an alias for -f/--file
346 # --follow is deprecated and now just an alias for -f/--file
347 # to mimic the behavior of Mercurial before version 1.5
347 # to mimic the behavior of Mercurial before version 1.5
348 opts['file'] = True
348 opts['file'] = True
349
349
350 if (not opts.get('user') and not opts.get('changeset')
350 if (not opts.get('user') and not opts.get('changeset')
351 and not opts.get('date') and not opts.get('file')):
351 and not opts.get('date') and not opts.get('file')):
352 opts['number'] = True
352 opts['number'] = True
353
353
354 linenumber = opts.get('line_number') is not None
354 linenumber = opts.get('line_number') is not None
355 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
355 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
356 raise error.Abort(_('at least one of -n/-c is required for -l'))
356 raise error.Abort(_('at least one of -n/-c is required for -l'))
357
357
358 rev = opts.get('rev')
358 rev = opts.get('rev')
359 if rev:
359 if rev:
360 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
360 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
361 ctx = scmutil.revsingle(repo, rev)
361 ctx = scmutil.revsingle(repo, rev)
362
362
363 ui.pager('annotate')
363 ui.pager('annotate')
364 rootfm = ui.formatter('annotate', opts)
364 rootfm = ui.formatter('annotate', opts)
365 if ui.debugflag:
365 if ui.debugflag:
366 shorthex = pycompat.identity
366 shorthex = pycompat.identity
367 else:
367 else:
368 def shorthex(h):
368 def shorthex(h):
369 return h[:12]
369 return h[:12]
370 if ui.quiet:
370 if ui.quiet:
371 datefunc = dateutil.shortdate
371 datefunc = dateutil.shortdate
372 else:
372 else:
373 datefunc = dateutil.datestr
373 datefunc = dateutil.datestr
374 if ctx.rev() is None:
374 if ctx.rev() is None:
375 if opts.get('changeset'):
375 if opts.get('changeset'):
376 # omit "+" suffix which is appended to node hex
376 # omit "+" suffix which is appended to node hex
377 def formatrev(rev):
377 def formatrev(rev):
378 if rev == wdirrev:
378 if rev == wdirrev:
379 return '%d' % ctx.p1().rev()
379 return '%d' % ctx.p1().rev()
380 else:
380 else:
381 return '%d' % rev
381 return '%d' % rev
382 else:
382 else:
383 def formatrev(rev):
383 def formatrev(rev):
384 if rev == wdirrev:
384 if rev == wdirrev:
385 return '%d+' % ctx.p1().rev()
385 return '%d+' % ctx.p1().rev()
386 else:
386 else:
387 return '%d ' % rev
387 return '%d ' % rev
388 def formathex(h):
388 def formathex(h):
389 if h == wdirhex:
389 if h == wdirhex:
390 return '%s+' % shorthex(hex(ctx.p1().node()))
390 return '%s+' % shorthex(hex(ctx.p1().node()))
391 else:
391 else:
392 return '%s ' % shorthex(h)
392 return '%s ' % shorthex(h)
393 else:
393 else:
394 formatrev = b'%d'.__mod__
394 formatrev = b'%d'.__mod__
395 formathex = shorthex
395 formathex = shorthex
396
396
397 opmap = [
397 opmap = [
398 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
398 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
399 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
399 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
400 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
400 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
401 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
401 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
402 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
402 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
403 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
403 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
404 ]
404 ]
405 opnamemap = {
405 opnamemap = {
406 'rev': 'number',
406 'rev': 'number',
407 'node': 'changeset',
407 'node': 'changeset',
408 'path': 'file',
408 'path': 'file',
409 'lineno': 'line_number',
409 'lineno': 'line_number',
410 }
410 }
411
411
412 if rootfm.isplain():
412 if rootfm.isplain():
413 def makefunc(get, fmt):
413 def makefunc(get, fmt):
414 return lambda x: fmt(get(x))
414 return lambda x: fmt(get(x))
415 else:
415 else:
416 def makefunc(get, fmt):
416 def makefunc(get, fmt):
417 return get
417 return get
418 datahint = rootfm.datahint()
418 datahint = rootfm.datahint()
419 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
419 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
420 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
420 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
421 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
421 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
422 fields = ' '.join(fn for fn, sep, get, fmt in opmap
422 fields = ' '.join(fn for fn, sep, get, fmt in opmap
423 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
423 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
424
424
425 def bad(x, y):
425 def bad(x, y):
426 raise error.Abort("%s: %s" % (x, y))
426 raise error.Abort("%s: %s" % (x, y))
427
427
428 m = scmutil.match(ctx, pats, opts, badfn=bad)
428 m = scmutil.match(ctx, pats, opts, badfn=bad)
429
429
430 follow = not opts.get('no_follow')
430 follow = not opts.get('no_follow')
431 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
431 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
432 whitespace=True)
432 whitespace=True)
433 skiprevs = opts.get('skip')
433 skiprevs = opts.get('skip')
434 if skiprevs:
434 if skiprevs:
435 skiprevs = scmutil.revrange(repo, skiprevs)
435 skiprevs = scmutil.revrange(repo, skiprevs)
436
436
437 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
437 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
438 for abs in ctx.walk(m):
438 for abs in ctx.walk(m):
439 fctx = ctx[abs]
439 fctx = ctx[abs]
440 rootfm.startitem()
440 rootfm.startitem()
441 rootfm.data(path=abs)
441 rootfm.data(path=abs)
442 if not opts.get('text') and fctx.isbinary():
442 if not opts.get('text') and fctx.isbinary():
443 rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
443 rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
444 continue
444 continue
445
445
446 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
446 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
447 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
447 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
448 diffopts=diffopts)
448 diffopts=diffopts)
449 if not lines:
449 if not lines:
450 fm.end()
450 fm.end()
451 continue
451 continue
452 formats = []
452 formats = []
453 pieces = []
453 pieces = []
454
454
455 for f, sep in funcmap:
455 for f, sep in funcmap:
456 l = [f(n) for n in lines]
456 l = [f(n) for n in lines]
457 if fm.isplain():
457 if fm.isplain():
458 sizes = [encoding.colwidth(x) for x in l]
458 sizes = [encoding.colwidth(x) for x in l]
459 ml = max(sizes)
459 ml = max(sizes)
460 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
460 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
461 else:
461 else:
462 formats.append(['%s' for x in l])
462 formats.append(['%s' for x in l])
463 pieces.append(l)
463 pieces.append(l)
464
464
465 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
465 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
466 fm.startitem()
466 fm.startitem()
467 fm.context(fctx=n.fctx)
467 fm.context(fctx=n.fctx)
468 fm.write(fields, "".join(f), *p)
468 fm.write(fields, "".join(f), *p)
469 if n.skip:
469 if n.skip:
470 fmt = "* %s"
470 fmt = "* %s"
471 else:
471 else:
472 fmt = ": %s"
472 fmt = ": %s"
473 fm.write('line', fmt, n.text)
473 fm.write('line', fmt, n.text)
474
474
475 if not lines[-1].text.endswith('\n'):
475 if not lines[-1].text.endswith('\n'):
476 fm.plain('\n')
476 fm.plain('\n')
477 fm.end()
477 fm.end()
478
478
479 rootfm.end()
479 rootfm.end()
480
480
481 @command('archive',
481 @command('archive',
482 [('', 'no-decode', None, _('do not pass files through decoders')),
482 [('', 'no-decode', None, _('do not pass files through decoders')),
483 ('p', 'prefix', '', _('directory prefix for files in archive'),
483 ('p', 'prefix', '', _('directory prefix for files in archive'),
484 _('PREFIX')),
484 _('PREFIX')),
485 ('r', 'rev', '', _('revision to distribute'), _('REV')),
485 ('r', 'rev', '', _('revision to distribute'), _('REV')),
486 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
486 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
487 ] + subrepoopts + walkopts,
487 ] + subrepoopts + walkopts,
488 _('[OPTION]... DEST'),
488 _('[OPTION]... DEST'),
489 helpcategory=command.CATEGORY_IMPORT_EXPORT)
489 helpcategory=command.CATEGORY_IMPORT_EXPORT)
490 def archive(ui, repo, dest, **opts):
490 def archive(ui, repo, dest, **opts):
491 '''create an unversioned archive of a repository revision
491 '''create an unversioned archive of a repository revision
492
492
493 By default, the revision used is the parent of the working
493 By default, the revision used is the parent of the working
494 directory; use -r/--rev to specify a different revision.
494 directory; use -r/--rev to specify a different revision.
495
495
496 The archive type is automatically detected based on file
496 The archive type is automatically detected based on file
497 extension (to override, use -t/--type).
497 extension (to override, use -t/--type).
498
498
499 .. container:: verbose
499 .. container:: verbose
500
500
501 Examples:
501 Examples:
502
502
503 - create a zip file containing the 1.0 release::
503 - create a zip file containing the 1.0 release::
504
504
505 hg archive -r 1.0 project-1.0.zip
505 hg archive -r 1.0 project-1.0.zip
506
506
507 - create a tarball excluding .hg files::
507 - create a tarball excluding .hg files::
508
508
509 hg archive project.tar.gz -X ".hg*"
509 hg archive project.tar.gz -X ".hg*"
510
510
511 Valid types are:
511 Valid types are:
512
512
513 :``files``: a directory full of files (default)
513 :``files``: a directory full of files (default)
514 :``tar``: tar archive, uncompressed
514 :``tar``: tar archive, uncompressed
515 :``tbz2``: tar archive, compressed using bzip2
515 :``tbz2``: tar archive, compressed using bzip2
516 :``tgz``: tar archive, compressed using gzip
516 :``tgz``: tar archive, compressed using gzip
517 :``txz``: tar archive, compressed using lzma (only in Python 3)
517 :``uzip``: zip archive, uncompressed
518 :``uzip``: zip archive, uncompressed
518 :``zip``: zip archive, compressed using deflate
519 :``zip``: zip archive, compressed using deflate
519
520
520 The exact name of the destination archive or directory is given
521 The exact name of the destination archive or directory is given
521 using a format string; see :hg:`help export` for details.
522 using a format string; see :hg:`help export` for details.
522
523
523 Each member added to an archive file has a directory prefix
524 Each member added to an archive file has a directory prefix
524 prepended. Use -p/--prefix to specify a format string for the
525 prepended. Use -p/--prefix to specify a format string for the
525 prefix. The default is the basename of the archive, with suffixes
526 prefix. The default is the basename of the archive, with suffixes
526 removed.
527 removed.
527
528
528 Returns 0 on success.
529 Returns 0 on success.
529 '''
530 '''
530
531
531 opts = pycompat.byteskwargs(opts)
532 opts = pycompat.byteskwargs(opts)
532 rev = opts.get('rev')
533 rev = opts.get('rev')
533 if rev:
534 if rev:
534 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
535 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
535 ctx = scmutil.revsingle(repo, rev)
536 ctx = scmutil.revsingle(repo, rev)
536 if not ctx:
537 if not ctx:
537 raise error.Abort(_('no working directory: please specify a revision'))
538 raise error.Abort(_('no working directory: please specify a revision'))
538 node = ctx.node()
539 node = ctx.node()
539 dest = cmdutil.makefilename(ctx, dest)
540 dest = cmdutil.makefilename(ctx, dest)
540 if os.path.realpath(dest) == repo.root:
541 if os.path.realpath(dest) == repo.root:
541 raise error.Abort(_('repository root cannot be destination'))
542 raise error.Abort(_('repository root cannot be destination'))
542
543
543 kind = opts.get('type') or archival.guesskind(dest) or 'files'
544 kind = opts.get('type') or archival.guesskind(dest) or 'files'
544 prefix = opts.get('prefix')
545 prefix = opts.get('prefix')
545
546
546 if dest == '-':
547 if dest == '-':
547 if kind == 'files':
548 if kind == 'files':
548 raise error.Abort(_('cannot archive plain files to stdout'))
549 raise error.Abort(_('cannot archive plain files to stdout'))
549 dest = cmdutil.makefileobj(ctx, dest)
550 dest = cmdutil.makefileobj(ctx, dest)
550 if not prefix:
551 if not prefix:
551 prefix = os.path.basename(repo.root) + '-%h'
552 prefix = os.path.basename(repo.root) + '-%h'
552
553
553 prefix = cmdutil.makefilename(ctx, prefix)
554 prefix = cmdutil.makefilename(ctx, prefix)
554 match = scmutil.match(ctx, [], opts)
555 match = scmutil.match(ctx, [], opts)
555 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
556 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
556 match, prefix, subrepos=opts.get('subrepos'))
557 match, prefix, subrepos=opts.get('subrepos'))
557
558
558 @command('backout',
559 @command('backout',
559 [('', 'merge', None, _('merge with old dirstate parent after backout')),
560 [('', 'merge', None, _('merge with old dirstate parent after backout')),
560 ('', 'commit', None,
561 ('', 'commit', None,
561 _('commit if no conflicts were encountered (DEPRECATED)')),
562 _('commit if no conflicts were encountered (DEPRECATED)')),
562 ('', 'no-commit', None, _('do not commit')),
563 ('', 'no-commit', None, _('do not commit')),
563 ('', 'parent', '',
564 ('', 'parent', '',
564 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
565 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
565 ('r', 'rev', '', _('revision to backout'), _('REV')),
566 ('r', 'rev', '', _('revision to backout'), _('REV')),
566 ('e', 'edit', False, _('invoke editor on commit messages')),
567 ('e', 'edit', False, _('invoke editor on commit messages')),
567 ] + mergetoolopts + walkopts + commitopts + commitopts2,
568 ] + mergetoolopts + walkopts + commitopts + commitopts2,
568 _('[OPTION]... [-r] REV'),
569 _('[OPTION]... [-r] REV'),
569 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
570 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
570 def backout(ui, repo, node=None, rev=None, **opts):
571 def backout(ui, repo, node=None, rev=None, **opts):
571 '''reverse effect of earlier changeset
572 '''reverse effect of earlier changeset
572
573
573 Prepare a new changeset with the effect of REV undone in the
574 Prepare a new changeset with the effect of REV undone in the
574 current working directory. If no conflicts were encountered,
575 current working directory. If no conflicts were encountered,
575 it will be committed immediately.
576 it will be committed immediately.
576
577
577 If REV is the parent of the working directory, then this new changeset
578 If REV is the parent of the working directory, then this new changeset
578 is committed automatically (unless --no-commit is specified).
579 is committed automatically (unless --no-commit is specified).
579
580
580 .. note::
581 .. note::
581
582
582 :hg:`backout` cannot be used to fix either an unwanted or
583 :hg:`backout` cannot be used to fix either an unwanted or
583 incorrect merge.
584 incorrect merge.
584
585
585 .. container:: verbose
586 .. container:: verbose
586
587
587 Examples:
588 Examples:
588
589
589 - Reverse the effect of the parent of the working directory.
590 - Reverse the effect of the parent of the working directory.
590 This backout will be committed immediately::
591 This backout will be committed immediately::
591
592
592 hg backout -r .
593 hg backout -r .
593
594
594 - Reverse the effect of previous bad revision 23::
595 - Reverse the effect of previous bad revision 23::
595
596
596 hg backout -r 23
597 hg backout -r 23
597
598
598 - Reverse the effect of previous bad revision 23 and
599 - Reverse the effect of previous bad revision 23 and
599 leave changes uncommitted::
600 leave changes uncommitted::
600
601
601 hg backout -r 23 --no-commit
602 hg backout -r 23 --no-commit
602 hg commit -m "Backout revision 23"
603 hg commit -m "Backout revision 23"
603
604
604 By default, the pending changeset will have one parent,
605 By default, the pending changeset will have one parent,
605 maintaining a linear history. With --merge, the pending
606 maintaining a linear history. With --merge, the pending
606 changeset will instead have two parents: the old parent of the
607 changeset will instead have two parents: the old parent of the
607 working directory and a new child of REV that simply undoes REV.
608 working directory and a new child of REV that simply undoes REV.
608
609
609 Before version 1.7, the behavior without --merge was equivalent
610 Before version 1.7, the behavior without --merge was equivalent
610 to specifying --merge followed by :hg:`update --clean .` to
611 to specifying --merge followed by :hg:`update --clean .` to
611 cancel the merge and leave the child of REV as a head to be
612 cancel the merge and leave the child of REV as a head to be
612 merged separately.
613 merged separately.
613
614
614 See :hg:`help dates` for a list of formats valid for -d/--date.
615 See :hg:`help dates` for a list of formats valid for -d/--date.
615
616
616 See :hg:`help revert` for a way to restore files to the state
617 See :hg:`help revert` for a way to restore files to the state
617 of another revision.
618 of another revision.
618
619
619 Returns 0 on success, 1 if nothing to backout or there are unresolved
620 Returns 0 on success, 1 if nothing to backout or there are unresolved
620 files.
621 files.
621 '''
622 '''
622 with repo.wlock(), repo.lock():
623 with repo.wlock(), repo.lock():
623 return _dobackout(ui, repo, node, rev, **opts)
624 return _dobackout(ui, repo, node, rev, **opts)
624
625
625 def _dobackout(ui, repo, node=None, rev=None, **opts):
626 def _dobackout(ui, repo, node=None, rev=None, **opts):
626 opts = pycompat.byteskwargs(opts)
627 opts = pycompat.byteskwargs(opts)
627 if opts.get('commit') and opts.get('no_commit'):
628 if opts.get('commit') and opts.get('no_commit'):
628 raise error.Abort(_("cannot use --commit with --no-commit"))
629 raise error.Abort(_("cannot use --commit with --no-commit"))
629 if opts.get('merge') and opts.get('no_commit'):
630 if opts.get('merge') and opts.get('no_commit'):
630 raise error.Abort(_("cannot use --merge with --no-commit"))
631 raise error.Abort(_("cannot use --merge with --no-commit"))
631
632
632 if rev and node:
633 if rev and node:
633 raise error.Abort(_("please specify just one revision"))
634 raise error.Abort(_("please specify just one revision"))
634
635
635 if not rev:
636 if not rev:
636 rev = node
637 rev = node
637
638
638 if not rev:
639 if not rev:
639 raise error.Abort(_("please specify a revision to backout"))
640 raise error.Abort(_("please specify a revision to backout"))
640
641
641 date = opts.get('date')
642 date = opts.get('date')
642 if date:
643 if date:
643 opts['date'] = dateutil.parsedate(date)
644 opts['date'] = dateutil.parsedate(date)
644
645
645 cmdutil.checkunfinished(repo)
646 cmdutil.checkunfinished(repo)
646 cmdutil.bailifchanged(repo)
647 cmdutil.bailifchanged(repo)
647 node = scmutil.revsingle(repo, rev).node()
648 node = scmutil.revsingle(repo, rev).node()
648
649
649 op1, op2 = repo.dirstate.parents()
650 op1, op2 = repo.dirstate.parents()
650 if not repo.changelog.isancestor(node, op1):
651 if not repo.changelog.isancestor(node, op1):
651 raise error.Abort(_('cannot backout change that is not an ancestor'))
652 raise error.Abort(_('cannot backout change that is not an ancestor'))
652
653
653 p1, p2 = repo.changelog.parents(node)
654 p1, p2 = repo.changelog.parents(node)
654 if p1 == nullid:
655 if p1 == nullid:
655 raise error.Abort(_('cannot backout a change with no parents'))
656 raise error.Abort(_('cannot backout a change with no parents'))
656 if p2 != nullid:
657 if p2 != nullid:
657 if not opts.get('parent'):
658 if not opts.get('parent'):
658 raise error.Abort(_('cannot backout a merge changeset'))
659 raise error.Abort(_('cannot backout a merge changeset'))
659 p = repo.lookup(opts['parent'])
660 p = repo.lookup(opts['parent'])
660 if p not in (p1, p2):
661 if p not in (p1, p2):
661 raise error.Abort(_('%s is not a parent of %s') %
662 raise error.Abort(_('%s is not a parent of %s') %
662 (short(p), short(node)))
663 (short(p), short(node)))
663 parent = p
664 parent = p
664 else:
665 else:
665 if opts.get('parent'):
666 if opts.get('parent'):
666 raise error.Abort(_('cannot use --parent on non-merge changeset'))
667 raise error.Abort(_('cannot use --parent on non-merge changeset'))
667 parent = p1
668 parent = p1
668
669
669 # the backout should appear on the same branch
670 # the backout should appear on the same branch
670 branch = repo.dirstate.branch()
671 branch = repo.dirstate.branch()
671 bheads = repo.branchheads(branch)
672 bheads = repo.branchheads(branch)
672 rctx = scmutil.revsingle(repo, hex(parent))
673 rctx = scmutil.revsingle(repo, hex(parent))
673 if not opts.get('merge') and op1 != node:
674 if not opts.get('merge') and op1 != node:
674 with dirstateguard.dirstateguard(repo, 'backout'):
675 with dirstateguard.dirstateguard(repo, 'backout'):
675 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
676 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
676 with ui.configoverride(overrides, 'backout'):
677 with ui.configoverride(overrides, 'backout'):
677 stats = mergemod.update(repo, parent, branchmerge=True,
678 stats = mergemod.update(repo, parent, branchmerge=True,
678 force=True, ancestor=node,
679 force=True, ancestor=node,
679 mergeancestor=False)
680 mergeancestor=False)
680 repo.setparents(op1, op2)
681 repo.setparents(op1, op2)
681 hg._showstats(repo, stats)
682 hg._showstats(repo, stats)
682 if stats.unresolvedcount:
683 if stats.unresolvedcount:
683 repo.ui.status(_("use 'hg resolve' to retry unresolved "
684 repo.ui.status(_("use 'hg resolve' to retry unresolved "
684 "file merges\n"))
685 "file merges\n"))
685 return 1
686 return 1
686 else:
687 else:
687 hg.clean(repo, node, show_stats=False)
688 hg.clean(repo, node, show_stats=False)
688 repo.dirstate.setbranch(branch)
689 repo.dirstate.setbranch(branch)
689 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
690 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
690
691
691 if opts.get('no_commit'):
692 if opts.get('no_commit'):
692 msg = _("changeset %s backed out, "
693 msg = _("changeset %s backed out, "
693 "don't forget to commit.\n")
694 "don't forget to commit.\n")
694 ui.status(msg % short(node))
695 ui.status(msg % short(node))
695 return 0
696 return 0
696
697
697 def commitfunc(ui, repo, message, match, opts):
698 def commitfunc(ui, repo, message, match, opts):
698 editform = 'backout'
699 editform = 'backout'
699 e = cmdutil.getcommiteditor(editform=editform,
700 e = cmdutil.getcommiteditor(editform=editform,
700 **pycompat.strkwargs(opts))
701 **pycompat.strkwargs(opts))
701 if not message:
702 if not message:
702 # we don't translate commit messages
703 # we don't translate commit messages
703 message = "Backed out changeset %s" % short(node)
704 message = "Backed out changeset %s" % short(node)
704 e = cmdutil.getcommiteditor(edit=True, editform=editform)
705 e = cmdutil.getcommiteditor(edit=True, editform=editform)
705 return repo.commit(message, opts.get('user'), opts.get('date'),
706 return repo.commit(message, opts.get('user'), opts.get('date'),
706 match, editor=e)
707 match, editor=e)
707 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
708 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
708 if not newnode:
709 if not newnode:
709 ui.status(_("nothing changed\n"))
710 ui.status(_("nothing changed\n"))
710 return 1
711 return 1
711 cmdutil.commitstatus(repo, newnode, branch, bheads)
712 cmdutil.commitstatus(repo, newnode, branch, bheads)
712
713
713 def nice(node):
714 def nice(node):
714 return '%d:%s' % (repo.changelog.rev(node), short(node))
715 return '%d:%s' % (repo.changelog.rev(node), short(node))
715 ui.status(_('changeset %s backs out changeset %s\n') %
716 ui.status(_('changeset %s backs out changeset %s\n') %
716 (nice(repo.changelog.tip()), nice(node)))
717 (nice(repo.changelog.tip()), nice(node)))
717 if opts.get('merge') and op1 != node:
718 if opts.get('merge') and op1 != node:
718 hg.clean(repo, op1, show_stats=False)
719 hg.clean(repo, op1, show_stats=False)
719 ui.status(_('merging with changeset %s\n')
720 ui.status(_('merging with changeset %s\n')
720 % nice(repo.changelog.tip()))
721 % nice(repo.changelog.tip()))
721 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
722 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
722 with ui.configoverride(overrides, 'backout'):
723 with ui.configoverride(overrides, 'backout'):
723 return hg.merge(repo, hex(repo.changelog.tip()))
724 return hg.merge(repo, hex(repo.changelog.tip()))
724 return 0
725 return 0
725
726
726 @command('bisect',
727 @command('bisect',
727 [('r', 'reset', False, _('reset bisect state')),
728 [('r', 'reset', False, _('reset bisect state')),
728 ('g', 'good', False, _('mark changeset good')),
729 ('g', 'good', False, _('mark changeset good')),
729 ('b', 'bad', False, _('mark changeset bad')),
730 ('b', 'bad', False, _('mark changeset bad')),
730 ('s', 'skip', False, _('skip testing changeset')),
731 ('s', 'skip', False, _('skip testing changeset')),
731 ('e', 'extend', False, _('extend the bisect range')),
732 ('e', 'extend', False, _('extend the bisect range')),
732 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
733 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
733 ('U', 'noupdate', False, _('do not update to target'))],
734 ('U', 'noupdate', False, _('do not update to target'))],
734 _("[-gbsr] [-U] [-c CMD] [REV]"),
735 _("[-gbsr] [-U] [-c CMD] [REV]"),
735 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
736 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
736 def bisect(ui, repo, rev=None, extra=None, command=None,
737 def bisect(ui, repo, rev=None, extra=None, command=None,
737 reset=None, good=None, bad=None, skip=None, extend=None,
738 reset=None, good=None, bad=None, skip=None, extend=None,
738 noupdate=None):
739 noupdate=None):
739 """subdivision search of changesets
740 """subdivision search of changesets
740
741
741 This command helps to find changesets which introduce problems. To
742 This command helps to find changesets which introduce problems. To
742 use, mark the earliest changeset you know exhibits the problem as
743 use, mark the earliest changeset you know exhibits the problem as
743 bad, then mark the latest changeset which is free from the problem
744 bad, then mark the latest changeset which is free from the problem
744 as good. Bisect will update your working directory to a revision
745 as good. Bisect will update your working directory to a revision
745 for testing (unless the -U/--noupdate option is specified). Once
746 for testing (unless the -U/--noupdate option is specified). Once
746 you have performed tests, mark the working directory as good or
747 you have performed tests, mark the working directory as good or
747 bad, and bisect will either update to another candidate changeset
748 bad, and bisect will either update to another candidate changeset
748 or announce that it has found the bad revision.
749 or announce that it has found the bad revision.
749
750
750 As a shortcut, you can also use the revision argument to mark a
751 As a shortcut, you can also use the revision argument to mark a
751 revision as good or bad without checking it out first.
752 revision as good or bad without checking it out first.
752
753
753 If you supply a command, it will be used for automatic bisection.
754 If you supply a command, it will be used for automatic bisection.
754 The environment variable HG_NODE will contain the ID of the
755 The environment variable HG_NODE will contain the ID of the
755 changeset being tested. The exit status of the command will be
756 changeset being tested. The exit status of the command will be
756 used to mark revisions as good or bad: status 0 means good, 125
757 used to mark revisions as good or bad: status 0 means good, 125
757 means to skip the revision, 127 (command not found) will abort the
758 means to skip the revision, 127 (command not found) will abort the
758 bisection, and any other non-zero exit status means the revision
759 bisection, and any other non-zero exit status means the revision
759 is bad.
760 is bad.
760
761
761 .. container:: verbose
762 .. container:: verbose
762
763
763 Some examples:
764 Some examples:
764
765
765 - start a bisection with known bad revision 34, and good revision 12::
766 - start a bisection with known bad revision 34, and good revision 12::
766
767
767 hg bisect --bad 34
768 hg bisect --bad 34
768 hg bisect --good 12
769 hg bisect --good 12
769
770
770 - advance the current bisection by marking current revision as good or
771 - advance the current bisection by marking current revision as good or
771 bad::
772 bad::
772
773
773 hg bisect --good
774 hg bisect --good
774 hg bisect --bad
775 hg bisect --bad
775
776
776 - mark the current revision, or a known revision, to be skipped (e.g. if
777 - mark the current revision, or a known revision, to be skipped (e.g. if
777 that revision is not usable because of another issue)::
778 that revision is not usable because of another issue)::
778
779
779 hg bisect --skip
780 hg bisect --skip
780 hg bisect --skip 23
781 hg bisect --skip 23
781
782
782 - skip all revisions that do not touch directories ``foo`` or ``bar``::
783 - skip all revisions that do not touch directories ``foo`` or ``bar``::
783
784
784 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
785 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
785
786
786 - forget the current bisection::
787 - forget the current bisection::
787
788
788 hg bisect --reset
789 hg bisect --reset
789
790
790 - use 'make && make tests' to automatically find the first broken
791 - use 'make && make tests' to automatically find the first broken
791 revision::
792 revision::
792
793
793 hg bisect --reset
794 hg bisect --reset
794 hg bisect --bad 34
795 hg bisect --bad 34
795 hg bisect --good 12
796 hg bisect --good 12
796 hg bisect --command "make && make tests"
797 hg bisect --command "make && make tests"
797
798
798 - see all changesets whose states are already known in the current
799 - see all changesets whose states are already known in the current
799 bisection::
800 bisection::
800
801
801 hg log -r "bisect(pruned)"
802 hg log -r "bisect(pruned)"
802
803
803 - see the changeset currently being bisected (especially useful
804 - see the changeset currently being bisected (especially useful
804 if running with -U/--noupdate)::
805 if running with -U/--noupdate)::
805
806
806 hg log -r "bisect(current)"
807 hg log -r "bisect(current)"
807
808
808 - see all changesets that took part in the current bisection::
809 - see all changesets that took part in the current bisection::
809
810
810 hg log -r "bisect(range)"
811 hg log -r "bisect(range)"
811
812
812 - you can even get a nice graph::
813 - you can even get a nice graph::
813
814
814 hg log --graph -r "bisect(range)"
815 hg log --graph -r "bisect(range)"
815
816
816 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
817 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
817
818
818 Returns 0 on success.
819 Returns 0 on success.
819 """
820 """
820 # backward compatibility
821 # backward compatibility
821 if rev in "good bad reset init".split():
822 if rev in "good bad reset init".split():
822 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
823 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
823 cmd, rev, extra = rev, extra, None
824 cmd, rev, extra = rev, extra, None
824 if cmd == "good":
825 if cmd == "good":
825 good = True
826 good = True
826 elif cmd == "bad":
827 elif cmd == "bad":
827 bad = True
828 bad = True
828 else:
829 else:
829 reset = True
830 reset = True
830 elif extra:
831 elif extra:
831 raise error.Abort(_('incompatible arguments'))
832 raise error.Abort(_('incompatible arguments'))
832
833
833 incompatibles = {
834 incompatibles = {
834 '--bad': bad,
835 '--bad': bad,
835 '--command': bool(command),
836 '--command': bool(command),
836 '--extend': extend,
837 '--extend': extend,
837 '--good': good,
838 '--good': good,
838 '--reset': reset,
839 '--reset': reset,
839 '--skip': skip,
840 '--skip': skip,
840 }
841 }
841
842
842 enabled = [x for x in incompatibles if incompatibles[x]]
843 enabled = [x for x in incompatibles if incompatibles[x]]
843
844
844 if len(enabled) > 1:
845 if len(enabled) > 1:
845 raise error.Abort(_('%s and %s are incompatible') %
846 raise error.Abort(_('%s and %s are incompatible') %
846 tuple(sorted(enabled)[0:2]))
847 tuple(sorted(enabled)[0:2]))
847
848
848 if reset:
849 if reset:
849 hbisect.resetstate(repo)
850 hbisect.resetstate(repo)
850 return
851 return
851
852
852 state = hbisect.load_state(repo)
853 state = hbisect.load_state(repo)
853
854
854 # update state
855 # update state
855 if good or bad or skip:
856 if good or bad or skip:
856 if rev:
857 if rev:
857 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
858 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
858 else:
859 else:
859 nodes = [repo.lookup('.')]
860 nodes = [repo.lookup('.')]
860 if good:
861 if good:
861 state['good'] += nodes
862 state['good'] += nodes
862 elif bad:
863 elif bad:
863 state['bad'] += nodes
864 state['bad'] += nodes
864 elif skip:
865 elif skip:
865 state['skip'] += nodes
866 state['skip'] += nodes
866 hbisect.save_state(repo, state)
867 hbisect.save_state(repo, state)
867 if not (state['good'] and state['bad']):
868 if not (state['good'] and state['bad']):
868 return
869 return
869
870
870 def mayupdate(repo, node, show_stats=True):
871 def mayupdate(repo, node, show_stats=True):
871 """common used update sequence"""
872 """common used update sequence"""
872 if noupdate:
873 if noupdate:
873 return
874 return
874 cmdutil.checkunfinished(repo)
875 cmdutil.checkunfinished(repo)
875 cmdutil.bailifchanged(repo)
876 cmdutil.bailifchanged(repo)
876 return hg.clean(repo, node, show_stats=show_stats)
877 return hg.clean(repo, node, show_stats=show_stats)
877
878
878 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
879 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
879
880
880 if command:
881 if command:
881 changesets = 1
882 changesets = 1
882 if noupdate:
883 if noupdate:
883 try:
884 try:
884 node = state['current'][0]
885 node = state['current'][0]
885 except LookupError:
886 except LookupError:
886 raise error.Abort(_('current bisect revision is unknown - '
887 raise error.Abort(_('current bisect revision is unknown - '
887 'start a new bisect to fix'))
888 'start a new bisect to fix'))
888 else:
889 else:
889 node, p2 = repo.dirstate.parents()
890 node, p2 = repo.dirstate.parents()
890 if p2 != nullid:
891 if p2 != nullid:
891 raise error.Abort(_('current bisect revision is a merge'))
892 raise error.Abort(_('current bisect revision is a merge'))
892 if rev:
893 if rev:
893 node = repo[scmutil.revsingle(repo, rev, node)].node()
894 node = repo[scmutil.revsingle(repo, rev, node)].node()
894 try:
895 try:
895 while changesets:
896 while changesets:
896 # update state
897 # update state
897 state['current'] = [node]
898 state['current'] = [node]
898 hbisect.save_state(repo, state)
899 hbisect.save_state(repo, state)
899 status = ui.system(command, environ={'HG_NODE': hex(node)},
900 status = ui.system(command, environ={'HG_NODE': hex(node)},
900 blockedtag='bisect_check')
901 blockedtag='bisect_check')
901 if status == 125:
902 if status == 125:
902 transition = "skip"
903 transition = "skip"
903 elif status == 0:
904 elif status == 0:
904 transition = "good"
905 transition = "good"
905 # status < 0 means process was killed
906 # status < 0 means process was killed
906 elif status == 127:
907 elif status == 127:
907 raise error.Abort(_("failed to execute %s") % command)
908 raise error.Abort(_("failed to execute %s") % command)
908 elif status < 0:
909 elif status < 0:
909 raise error.Abort(_("%s killed") % command)
910 raise error.Abort(_("%s killed") % command)
910 else:
911 else:
911 transition = "bad"
912 transition = "bad"
912 state[transition].append(node)
913 state[transition].append(node)
913 ctx = repo[node]
914 ctx = repo[node]
914 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
915 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
915 transition))
916 transition))
916 hbisect.checkstate(state)
917 hbisect.checkstate(state)
917 # bisect
918 # bisect
918 nodes, changesets, bgood = hbisect.bisect(repo, state)
919 nodes, changesets, bgood = hbisect.bisect(repo, state)
919 # update to next check
920 # update to next check
920 node = nodes[0]
921 node = nodes[0]
921 mayupdate(repo, node, show_stats=False)
922 mayupdate(repo, node, show_stats=False)
922 finally:
923 finally:
923 state['current'] = [node]
924 state['current'] = [node]
924 hbisect.save_state(repo, state)
925 hbisect.save_state(repo, state)
925 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
926 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
926 return
927 return
927
928
928 hbisect.checkstate(state)
929 hbisect.checkstate(state)
929
930
930 # actually bisect
931 # actually bisect
931 nodes, changesets, good = hbisect.bisect(repo, state)
932 nodes, changesets, good = hbisect.bisect(repo, state)
932 if extend:
933 if extend:
933 if not changesets:
934 if not changesets:
934 extendnode = hbisect.extendrange(repo, state, nodes, good)
935 extendnode = hbisect.extendrange(repo, state, nodes, good)
935 if extendnode is not None:
936 if extendnode is not None:
936 ui.write(_("Extending search to changeset %d:%s\n")
937 ui.write(_("Extending search to changeset %d:%s\n")
937 % (extendnode.rev(), extendnode))
938 % (extendnode.rev(), extendnode))
938 state['current'] = [extendnode.node()]
939 state['current'] = [extendnode.node()]
939 hbisect.save_state(repo, state)
940 hbisect.save_state(repo, state)
940 return mayupdate(repo, extendnode.node())
941 return mayupdate(repo, extendnode.node())
941 raise error.Abort(_("nothing to extend"))
942 raise error.Abort(_("nothing to extend"))
942
943
943 if changesets == 0:
944 if changesets == 0:
944 hbisect.printresult(ui, repo, state, displayer, nodes, good)
945 hbisect.printresult(ui, repo, state, displayer, nodes, good)
945 else:
946 else:
946 assert len(nodes) == 1 # only a single node can be tested next
947 assert len(nodes) == 1 # only a single node can be tested next
947 node = nodes[0]
948 node = nodes[0]
948 # compute the approximate number of remaining tests
949 # compute the approximate number of remaining tests
949 tests, size = 0, 2
950 tests, size = 0, 2
950 while size <= changesets:
951 while size <= changesets:
951 tests, size = tests + 1, size * 2
952 tests, size = tests + 1, size * 2
952 rev = repo.changelog.rev(node)
953 rev = repo.changelog.rev(node)
953 ui.write(_("Testing changeset %d:%s "
954 ui.write(_("Testing changeset %d:%s "
954 "(%d changesets remaining, ~%d tests)\n")
955 "(%d changesets remaining, ~%d tests)\n")
955 % (rev, short(node), changesets, tests))
956 % (rev, short(node), changesets, tests))
956 state['current'] = [node]
957 state['current'] = [node]
957 hbisect.save_state(repo, state)
958 hbisect.save_state(repo, state)
958 return mayupdate(repo, node)
959 return mayupdate(repo, node)
959
960
960 @command('bookmarks|bookmark',
961 @command('bookmarks|bookmark',
961 [('f', 'force', False, _('force')),
962 [('f', 'force', False, _('force')),
962 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
963 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
963 ('d', 'delete', False, _('delete a given bookmark')),
964 ('d', 'delete', False, _('delete a given bookmark')),
964 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
965 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
965 ('i', 'inactive', False, _('mark a bookmark inactive')),
966 ('i', 'inactive', False, _('mark a bookmark inactive')),
966 ('l', 'list', False, _('list existing bookmarks')),
967 ('l', 'list', False, _('list existing bookmarks')),
967 ] + formatteropts,
968 ] + formatteropts,
968 _('hg bookmarks [OPTIONS]... [NAME]...'),
969 _('hg bookmarks [OPTIONS]... [NAME]...'),
969 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
970 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
970 def bookmark(ui, repo, *names, **opts):
971 def bookmark(ui, repo, *names, **opts):
971 '''create a new bookmark or list existing bookmarks
972 '''create a new bookmark or list existing bookmarks
972
973
973 Bookmarks are labels on changesets to help track lines of development.
974 Bookmarks are labels on changesets to help track lines of development.
974 Bookmarks are unversioned and can be moved, renamed and deleted.
975 Bookmarks are unversioned and can be moved, renamed and deleted.
975 Deleting or moving a bookmark has no effect on the associated changesets.
976 Deleting or moving a bookmark has no effect on the associated changesets.
976
977
977 Creating or updating to a bookmark causes it to be marked as 'active'.
978 Creating or updating to a bookmark causes it to be marked as 'active'.
978 The active bookmark is indicated with a '*'.
979 The active bookmark is indicated with a '*'.
979 When a commit is made, the active bookmark will advance to the new commit.
980 When a commit is made, the active bookmark will advance to the new commit.
980 A plain :hg:`update` will also advance an active bookmark, if possible.
981 A plain :hg:`update` will also advance an active bookmark, if possible.
981 Updating away from a bookmark will cause it to be deactivated.
982 Updating away from a bookmark will cause it to be deactivated.
982
983
983 Bookmarks can be pushed and pulled between repositories (see
984 Bookmarks can be pushed and pulled between repositories (see
984 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
985 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
985 diverged, a new 'divergent bookmark' of the form 'name@path' will
986 diverged, a new 'divergent bookmark' of the form 'name@path' will
986 be created. Using :hg:`merge` will resolve the divergence.
987 be created. Using :hg:`merge` will resolve the divergence.
987
988
988 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
989 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
989 the active bookmark's name.
990 the active bookmark's name.
990
991
991 A bookmark named '@' has the special property that :hg:`clone` will
992 A bookmark named '@' has the special property that :hg:`clone` will
992 check it out by default if it exists.
993 check it out by default if it exists.
993
994
994 .. container:: verbose
995 .. container:: verbose
995
996
996 Template:
997 Template:
997
998
998 The following keywords are supported in addition to the common template
999 The following keywords are supported in addition to the common template
999 keywords and functions such as ``{bookmark}``. See also
1000 keywords and functions such as ``{bookmark}``. See also
1000 :hg:`help templates`.
1001 :hg:`help templates`.
1001
1002
1002 :active: Boolean. True if the bookmark is active.
1003 :active: Boolean. True if the bookmark is active.
1003
1004
1004 Examples:
1005 Examples:
1005
1006
1006 - create an active bookmark for a new line of development::
1007 - create an active bookmark for a new line of development::
1007
1008
1008 hg book new-feature
1009 hg book new-feature
1009
1010
1010 - create an inactive bookmark as a place marker::
1011 - create an inactive bookmark as a place marker::
1011
1012
1012 hg book -i reviewed
1013 hg book -i reviewed
1013
1014
1014 - create an inactive bookmark on another changeset::
1015 - create an inactive bookmark on another changeset::
1015
1016
1016 hg book -r .^ tested
1017 hg book -r .^ tested
1017
1018
1018 - rename bookmark turkey to dinner::
1019 - rename bookmark turkey to dinner::
1019
1020
1020 hg book -m turkey dinner
1021 hg book -m turkey dinner
1021
1022
1022 - move the '@' bookmark from another branch::
1023 - move the '@' bookmark from another branch::
1023
1024
1024 hg book -f @
1025 hg book -f @
1025
1026
1026 - print only the active bookmark name::
1027 - print only the active bookmark name::
1027
1028
1028 hg book -ql .
1029 hg book -ql .
1029 '''
1030 '''
1030 opts = pycompat.byteskwargs(opts)
1031 opts = pycompat.byteskwargs(opts)
1031 force = opts.get('force')
1032 force = opts.get('force')
1032 rev = opts.get('rev')
1033 rev = opts.get('rev')
1033 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1034 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1034
1035
1035 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1036 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1036 if len(selactions) > 1:
1037 if len(selactions) > 1:
1037 raise error.Abort(_('--%s and --%s are incompatible')
1038 raise error.Abort(_('--%s and --%s are incompatible')
1038 % tuple(selactions[:2]))
1039 % tuple(selactions[:2]))
1039 if selactions:
1040 if selactions:
1040 action = selactions[0]
1041 action = selactions[0]
1041 elif names or rev:
1042 elif names or rev:
1042 action = 'add'
1043 action = 'add'
1043 elif inactive:
1044 elif inactive:
1044 action = 'inactive' # meaning deactivate
1045 action = 'inactive' # meaning deactivate
1045 else:
1046 else:
1046 action = 'list'
1047 action = 'list'
1047
1048
1048 if rev and action in {'delete', 'rename', 'list'}:
1049 if rev and action in {'delete', 'rename', 'list'}:
1049 raise error.Abort(_("--rev is incompatible with --%s") % action)
1050 raise error.Abort(_("--rev is incompatible with --%s") % action)
1050 if inactive and action in {'delete', 'list'}:
1051 if inactive and action in {'delete', 'list'}:
1051 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1052 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1052 if not names and action in {'add', 'delete'}:
1053 if not names and action in {'add', 'delete'}:
1053 raise error.Abort(_("bookmark name required"))
1054 raise error.Abort(_("bookmark name required"))
1054
1055
1055 if action in {'add', 'delete', 'rename', 'inactive'}:
1056 if action in {'add', 'delete', 'rename', 'inactive'}:
1056 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1057 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1057 if action == 'delete':
1058 if action == 'delete':
1058 names = pycompat.maplist(repo._bookmarks.expandname, names)
1059 names = pycompat.maplist(repo._bookmarks.expandname, names)
1059 bookmarks.delete(repo, tr, names)
1060 bookmarks.delete(repo, tr, names)
1060 elif action == 'rename':
1061 elif action == 'rename':
1061 if not names:
1062 if not names:
1062 raise error.Abort(_("new bookmark name required"))
1063 raise error.Abort(_("new bookmark name required"))
1063 elif len(names) > 1:
1064 elif len(names) > 1:
1064 raise error.Abort(_("only one new bookmark name allowed"))
1065 raise error.Abort(_("only one new bookmark name allowed"))
1065 oldname = repo._bookmarks.expandname(opts['rename'])
1066 oldname = repo._bookmarks.expandname(opts['rename'])
1066 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1067 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1067 elif action == 'add':
1068 elif action == 'add':
1068 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1069 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1069 elif action == 'inactive':
1070 elif action == 'inactive':
1070 if len(repo._bookmarks) == 0:
1071 if len(repo._bookmarks) == 0:
1071 ui.status(_("no bookmarks set\n"))
1072 ui.status(_("no bookmarks set\n"))
1072 elif not repo._activebookmark:
1073 elif not repo._activebookmark:
1073 ui.status(_("no active bookmark\n"))
1074 ui.status(_("no active bookmark\n"))
1074 else:
1075 else:
1075 bookmarks.deactivate(repo)
1076 bookmarks.deactivate(repo)
1076 elif action == 'list':
1077 elif action == 'list':
1077 names = pycompat.maplist(repo._bookmarks.expandname, names)
1078 names = pycompat.maplist(repo._bookmarks.expandname, names)
1078 with ui.formatter('bookmarks', opts) as fm:
1079 with ui.formatter('bookmarks', opts) as fm:
1079 bookmarks.printbookmarks(ui, repo, fm, names)
1080 bookmarks.printbookmarks(ui, repo, fm, names)
1080 else:
1081 else:
1081 raise error.ProgrammingError('invalid action: %s' % action)
1082 raise error.ProgrammingError('invalid action: %s' % action)
1082
1083
1083 @command('branch',
1084 @command('branch',
1084 [('f', 'force', None,
1085 [('f', 'force', None,
1085 _('set branch name even if it shadows an existing branch')),
1086 _('set branch name even if it shadows an existing branch')),
1086 ('C', 'clean', None, _('reset branch name to parent branch name')),
1087 ('C', 'clean', None, _('reset branch name to parent branch name')),
1087 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1088 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1088 ],
1089 ],
1089 _('[-fC] [NAME]'),
1090 _('[-fC] [NAME]'),
1090 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1091 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1091 def branch(ui, repo, label=None, **opts):
1092 def branch(ui, repo, label=None, **opts):
1092 """set or show the current branch name
1093 """set or show the current branch name
1093
1094
1094 .. note::
1095 .. note::
1095
1096
1096 Branch names are permanent and global. Use :hg:`bookmark` to create a
1097 Branch names are permanent and global. Use :hg:`bookmark` to create a
1097 light-weight bookmark instead. See :hg:`help glossary` for more
1098 light-weight bookmark instead. See :hg:`help glossary` for more
1098 information about named branches and bookmarks.
1099 information about named branches and bookmarks.
1099
1100
1100 With no argument, show the current branch name. With one argument,
1101 With no argument, show the current branch name. With one argument,
1101 set the working directory branch name (the branch will not exist
1102 set the working directory branch name (the branch will not exist
1102 in the repository until the next commit). Standard practice
1103 in the repository until the next commit). Standard practice
1103 recommends that primary development take place on the 'default'
1104 recommends that primary development take place on the 'default'
1104 branch.
1105 branch.
1105
1106
1106 Unless -f/--force is specified, branch will not let you set a
1107 Unless -f/--force is specified, branch will not let you set a
1107 branch name that already exists.
1108 branch name that already exists.
1108
1109
1109 Use -C/--clean to reset the working directory branch to that of
1110 Use -C/--clean to reset the working directory branch to that of
1110 the parent of the working directory, negating a previous branch
1111 the parent of the working directory, negating a previous branch
1111 change.
1112 change.
1112
1113
1113 Use the command :hg:`update` to switch to an existing branch. Use
1114 Use the command :hg:`update` to switch to an existing branch. Use
1114 :hg:`commit --close-branch` to mark this branch head as closed.
1115 :hg:`commit --close-branch` to mark this branch head as closed.
1115 When all heads of a branch are closed, the branch will be
1116 When all heads of a branch are closed, the branch will be
1116 considered closed.
1117 considered closed.
1117
1118
1118 Returns 0 on success.
1119 Returns 0 on success.
1119 """
1120 """
1120 opts = pycompat.byteskwargs(opts)
1121 opts = pycompat.byteskwargs(opts)
1121 revs = opts.get('rev')
1122 revs = opts.get('rev')
1122 if label:
1123 if label:
1123 label = label.strip()
1124 label = label.strip()
1124
1125
1125 if not opts.get('clean') and not label:
1126 if not opts.get('clean') and not label:
1126 if revs:
1127 if revs:
1127 raise error.Abort(_("no branch name specified for the revisions"))
1128 raise error.Abort(_("no branch name specified for the revisions"))
1128 ui.write("%s\n" % repo.dirstate.branch())
1129 ui.write("%s\n" % repo.dirstate.branch())
1129 return
1130 return
1130
1131
1131 with repo.wlock():
1132 with repo.wlock():
1132 if opts.get('clean'):
1133 if opts.get('clean'):
1133 label = repo['.'].branch()
1134 label = repo['.'].branch()
1134 repo.dirstate.setbranch(label)
1135 repo.dirstate.setbranch(label)
1135 ui.status(_('reset working directory to branch %s\n') % label)
1136 ui.status(_('reset working directory to branch %s\n') % label)
1136 elif label:
1137 elif label:
1137
1138
1138 scmutil.checknewlabel(repo, label, 'branch')
1139 scmutil.checknewlabel(repo, label, 'branch')
1139 if revs:
1140 if revs:
1140 return cmdutil.changebranch(ui, repo, revs, label)
1141 return cmdutil.changebranch(ui, repo, revs, label)
1141
1142
1142 if not opts.get('force') and label in repo.branchmap():
1143 if not opts.get('force') and label in repo.branchmap():
1143 if label not in [p.branch() for p in repo[None].parents()]:
1144 if label not in [p.branch() for p in repo[None].parents()]:
1144 raise error.Abort(_('a branch of the same name already'
1145 raise error.Abort(_('a branch of the same name already'
1145 ' exists'),
1146 ' exists'),
1146 # i18n: "it" refers to an existing branch
1147 # i18n: "it" refers to an existing branch
1147 hint=_("use 'hg update' to switch to it"))
1148 hint=_("use 'hg update' to switch to it"))
1148
1149
1149 repo.dirstate.setbranch(label)
1150 repo.dirstate.setbranch(label)
1150 ui.status(_('marked working directory as branch %s\n') % label)
1151 ui.status(_('marked working directory as branch %s\n') % label)
1151
1152
1152 # find any open named branches aside from default
1153 # find any open named branches aside from default
1153 for n, h, t, c in repo.branchmap().iterbranches():
1154 for n, h, t, c in repo.branchmap().iterbranches():
1154 if n != "default" and not c:
1155 if n != "default" and not c:
1155 return 0
1156 return 0
1156 ui.status(_('(branches are permanent and global, '
1157 ui.status(_('(branches are permanent and global, '
1157 'did you want a bookmark?)\n'))
1158 'did you want a bookmark?)\n'))
1158
1159
1159 @command('branches',
1160 @command('branches',
1160 [('a', 'active', False,
1161 [('a', 'active', False,
1161 _('show only branches that have unmerged heads (DEPRECATED)')),
1162 _('show only branches that have unmerged heads (DEPRECATED)')),
1162 ('c', 'closed', False, _('show normal and closed branches')),
1163 ('c', 'closed', False, _('show normal and closed branches')),
1163 ('r', 'rev', [], _('show branch name(s) of the given rev'))
1164 ('r', 'rev', [], _('show branch name(s) of the given rev'))
1164 ] + formatteropts,
1165 ] + formatteropts,
1165 _('[-c]'),
1166 _('[-c]'),
1166 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1167 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1167 intents={INTENT_READONLY})
1168 intents={INTENT_READONLY})
1168 def branches(ui, repo, active=False, closed=False, **opts):
1169 def branches(ui, repo, active=False, closed=False, **opts):
1169 """list repository named branches
1170 """list repository named branches
1170
1171
1171 List the repository's named branches, indicating which ones are
1172 List the repository's named branches, indicating which ones are
1172 inactive. If -c/--closed is specified, also list branches which have
1173 inactive. If -c/--closed is specified, also list branches which have
1173 been marked closed (see :hg:`commit --close-branch`).
1174 been marked closed (see :hg:`commit --close-branch`).
1174
1175
1175 Use the command :hg:`update` to switch to an existing branch.
1176 Use the command :hg:`update` to switch to an existing branch.
1176
1177
1177 .. container:: verbose
1178 .. container:: verbose
1178
1179
1179 Template:
1180 Template:
1180
1181
1181 The following keywords are supported in addition to the common template
1182 The following keywords are supported in addition to the common template
1182 keywords and functions such as ``{branch}``. See also
1183 keywords and functions such as ``{branch}``. See also
1183 :hg:`help templates`.
1184 :hg:`help templates`.
1184
1185
1185 :active: Boolean. True if the branch is active.
1186 :active: Boolean. True if the branch is active.
1186 :closed: Boolean. True if the branch is closed.
1187 :closed: Boolean. True if the branch is closed.
1187 :current: Boolean. True if it is the current branch.
1188 :current: Boolean. True if it is the current branch.
1188
1189
1189 Returns 0.
1190 Returns 0.
1190 """
1191 """
1191
1192
1192 opts = pycompat.byteskwargs(opts)
1193 opts = pycompat.byteskwargs(opts)
1193 revs = opts.get('rev')
1194 revs = opts.get('rev')
1194 selectedbranches = None
1195 selectedbranches = None
1195 if revs:
1196 if revs:
1196 revs = scmutil.revrange(repo, revs)
1197 revs = scmutil.revrange(repo, revs)
1197 getbi = repo.revbranchcache().branchinfo
1198 getbi = repo.revbranchcache().branchinfo
1198 selectedbranches = {getbi(r)[0] for r in revs}
1199 selectedbranches = {getbi(r)[0] for r in revs}
1199
1200
1200 ui.pager('branches')
1201 ui.pager('branches')
1201 fm = ui.formatter('branches', opts)
1202 fm = ui.formatter('branches', opts)
1202 hexfunc = fm.hexfunc
1203 hexfunc = fm.hexfunc
1203
1204
1204 allheads = set(repo.heads())
1205 allheads = set(repo.heads())
1205 branches = []
1206 branches = []
1206 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1207 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1207 if selectedbranches is not None and tag not in selectedbranches:
1208 if selectedbranches is not None and tag not in selectedbranches:
1208 continue
1209 continue
1209 isactive = False
1210 isactive = False
1210 if not isclosed:
1211 if not isclosed:
1211 openheads = set(repo.branchmap().iteropen(heads))
1212 openheads = set(repo.branchmap().iteropen(heads))
1212 isactive = bool(openheads & allheads)
1213 isactive = bool(openheads & allheads)
1213 branches.append((tag, repo[tip], isactive, not isclosed))
1214 branches.append((tag, repo[tip], isactive, not isclosed))
1214 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1215 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1215 reverse=True)
1216 reverse=True)
1216
1217
1217 for tag, ctx, isactive, isopen in branches:
1218 for tag, ctx, isactive, isopen in branches:
1218 if active and not isactive:
1219 if active and not isactive:
1219 continue
1220 continue
1220 if isactive:
1221 if isactive:
1221 label = 'branches.active'
1222 label = 'branches.active'
1222 notice = ''
1223 notice = ''
1223 elif not isopen:
1224 elif not isopen:
1224 if not closed:
1225 if not closed:
1225 continue
1226 continue
1226 label = 'branches.closed'
1227 label = 'branches.closed'
1227 notice = _(' (closed)')
1228 notice = _(' (closed)')
1228 else:
1229 else:
1229 label = 'branches.inactive'
1230 label = 'branches.inactive'
1230 notice = _(' (inactive)')
1231 notice = _(' (inactive)')
1231 current = (tag == repo.dirstate.branch())
1232 current = (tag == repo.dirstate.branch())
1232 if current:
1233 if current:
1233 label = 'branches.current'
1234 label = 'branches.current'
1234
1235
1235 fm.startitem()
1236 fm.startitem()
1236 fm.write('branch', '%s', tag, label=label)
1237 fm.write('branch', '%s', tag, label=label)
1237 rev = ctx.rev()
1238 rev = ctx.rev()
1238 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1239 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1239 fmt = ' ' * padsize + ' %d:%s'
1240 fmt = ' ' * padsize + ' %d:%s'
1240 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1241 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1241 label='log.changeset changeset.%s' % ctx.phasestr())
1242 label='log.changeset changeset.%s' % ctx.phasestr())
1242 fm.context(ctx=ctx)
1243 fm.context(ctx=ctx)
1243 fm.data(active=isactive, closed=not isopen, current=current)
1244 fm.data(active=isactive, closed=not isopen, current=current)
1244 if not ui.quiet:
1245 if not ui.quiet:
1245 fm.plain(notice)
1246 fm.plain(notice)
1246 fm.plain('\n')
1247 fm.plain('\n')
1247 fm.end()
1248 fm.end()
1248
1249
1249 @command('bundle',
1250 @command('bundle',
1250 [('f', 'force', None, _('run even when the destination is unrelated')),
1251 [('f', 'force', None, _('run even when the destination is unrelated')),
1251 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1252 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1252 _('REV')),
1253 _('REV')),
1253 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1254 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1254 _('BRANCH')),
1255 _('BRANCH')),
1255 ('', 'base', [],
1256 ('', 'base', [],
1256 _('a base changeset assumed to be available at the destination'),
1257 _('a base changeset assumed to be available at the destination'),
1257 _('REV')),
1258 _('REV')),
1258 ('a', 'all', None, _('bundle all changesets in the repository')),
1259 ('a', 'all', None, _('bundle all changesets in the repository')),
1259 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1260 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1260 ] + remoteopts,
1261 ] + remoteopts,
1261 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1262 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1262 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1263 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1263 def bundle(ui, repo, fname, dest=None, **opts):
1264 def bundle(ui, repo, fname, dest=None, **opts):
1264 """create a bundle file
1265 """create a bundle file
1265
1266
1266 Generate a bundle file containing data to be transferred to another
1267 Generate a bundle file containing data to be transferred to another
1267 repository.
1268 repository.
1268
1269
1269 To create a bundle containing all changesets, use -a/--all
1270 To create a bundle containing all changesets, use -a/--all
1270 (or --base null). Otherwise, hg assumes the destination will have
1271 (or --base null). Otherwise, hg assumes the destination will have
1271 all the nodes you specify with --base parameters. Otherwise, hg
1272 all the nodes you specify with --base parameters. Otherwise, hg
1272 will assume the repository has all the nodes in destination, or
1273 will assume the repository has all the nodes in destination, or
1273 default-push/default if no destination is specified, where destination
1274 default-push/default if no destination is specified, where destination
1274 is the repository you provide through DEST option.
1275 is the repository you provide through DEST option.
1275
1276
1276 You can change bundle format with the -t/--type option. See
1277 You can change bundle format with the -t/--type option. See
1277 :hg:`help bundlespec` for documentation on this format. By default,
1278 :hg:`help bundlespec` for documentation on this format. By default,
1278 the most appropriate format is used and compression defaults to
1279 the most appropriate format is used and compression defaults to
1279 bzip2.
1280 bzip2.
1280
1281
1281 The bundle file can then be transferred using conventional means
1282 The bundle file can then be transferred using conventional means
1282 and applied to another repository with the unbundle or pull
1283 and applied to another repository with the unbundle or pull
1283 command. This is useful when direct push and pull are not
1284 command. This is useful when direct push and pull are not
1284 available or when exporting an entire repository is undesirable.
1285 available or when exporting an entire repository is undesirable.
1285
1286
1286 Applying bundles preserves all changeset contents including
1287 Applying bundles preserves all changeset contents including
1287 permissions, copy/rename information, and revision history.
1288 permissions, copy/rename information, and revision history.
1288
1289
1289 Returns 0 on success, 1 if no changes found.
1290 Returns 0 on success, 1 if no changes found.
1290 """
1291 """
1291 opts = pycompat.byteskwargs(opts)
1292 opts = pycompat.byteskwargs(opts)
1292 revs = None
1293 revs = None
1293 if 'rev' in opts:
1294 if 'rev' in opts:
1294 revstrings = opts['rev']
1295 revstrings = opts['rev']
1295 revs = scmutil.revrange(repo, revstrings)
1296 revs = scmutil.revrange(repo, revstrings)
1296 if revstrings and not revs:
1297 if revstrings and not revs:
1297 raise error.Abort(_('no commits to bundle'))
1298 raise error.Abort(_('no commits to bundle'))
1298
1299
1299 bundletype = opts.get('type', 'bzip2').lower()
1300 bundletype = opts.get('type', 'bzip2').lower()
1300 try:
1301 try:
1301 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1302 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1302 except error.UnsupportedBundleSpecification as e:
1303 except error.UnsupportedBundleSpecification as e:
1303 raise error.Abort(pycompat.bytestr(e),
1304 raise error.Abort(pycompat.bytestr(e),
1304 hint=_("see 'hg help bundlespec' for supported "
1305 hint=_("see 'hg help bundlespec' for supported "
1305 "values for --type"))
1306 "values for --type"))
1306 cgversion = bundlespec.contentopts["cg.version"]
1307 cgversion = bundlespec.contentopts["cg.version"]
1307
1308
1308 # Packed bundles are a pseudo bundle format for now.
1309 # Packed bundles are a pseudo bundle format for now.
1309 if cgversion == 's1':
1310 if cgversion == 's1':
1310 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1311 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1311 hint=_("use 'hg debugcreatestreamclonebundle'"))
1312 hint=_("use 'hg debugcreatestreamclonebundle'"))
1312
1313
1313 if opts.get('all'):
1314 if opts.get('all'):
1314 if dest:
1315 if dest:
1315 raise error.Abort(_("--all is incompatible with specifying "
1316 raise error.Abort(_("--all is incompatible with specifying "
1316 "a destination"))
1317 "a destination"))
1317 if opts.get('base'):
1318 if opts.get('base'):
1318 ui.warn(_("ignoring --base because --all was specified\n"))
1319 ui.warn(_("ignoring --base because --all was specified\n"))
1319 base = [nullrev]
1320 base = [nullrev]
1320 else:
1321 else:
1321 base = scmutil.revrange(repo, opts.get('base'))
1322 base = scmutil.revrange(repo, opts.get('base'))
1322 if cgversion not in changegroup.supportedoutgoingversions(repo):
1323 if cgversion not in changegroup.supportedoutgoingversions(repo):
1323 raise error.Abort(_("repository does not support bundle version %s") %
1324 raise error.Abort(_("repository does not support bundle version %s") %
1324 cgversion)
1325 cgversion)
1325
1326
1326 if base:
1327 if base:
1327 if dest:
1328 if dest:
1328 raise error.Abort(_("--base is incompatible with specifying "
1329 raise error.Abort(_("--base is incompatible with specifying "
1329 "a destination"))
1330 "a destination"))
1330 common = [repo[rev].node() for rev in base]
1331 common = [repo[rev].node() for rev in base]
1331 heads = [repo[r].node() for r in revs] if revs else None
1332 heads = [repo[r].node() for r in revs] if revs else None
1332 outgoing = discovery.outgoing(repo, common, heads)
1333 outgoing = discovery.outgoing(repo, common, heads)
1333 else:
1334 else:
1334 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1335 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1335 dest, branches = hg.parseurl(dest, opts.get('branch'))
1336 dest, branches = hg.parseurl(dest, opts.get('branch'))
1336 other = hg.peer(repo, opts, dest)
1337 other = hg.peer(repo, opts, dest)
1337 revs = [repo[r].hex() for r in revs]
1338 revs = [repo[r].hex() for r in revs]
1338 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1339 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1339 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1340 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1340 outgoing = discovery.findcommonoutgoing(repo, other,
1341 outgoing = discovery.findcommonoutgoing(repo, other,
1341 onlyheads=heads,
1342 onlyheads=heads,
1342 force=opts.get('force'),
1343 force=opts.get('force'),
1343 portable=True)
1344 portable=True)
1344
1345
1345 if not outgoing.missing:
1346 if not outgoing.missing:
1346 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1347 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1347 return 1
1348 return 1
1348
1349
1349 if cgversion == '01': #bundle1
1350 if cgversion == '01': #bundle1
1350 bversion = 'HG10' + bundlespec.wirecompression
1351 bversion = 'HG10' + bundlespec.wirecompression
1351 bcompression = None
1352 bcompression = None
1352 elif cgversion in ('02', '03'):
1353 elif cgversion in ('02', '03'):
1353 bversion = 'HG20'
1354 bversion = 'HG20'
1354 bcompression = bundlespec.wirecompression
1355 bcompression = bundlespec.wirecompression
1355 else:
1356 else:
1356 raise error.ProgrammingError(
1357 raise error.ProgrammingError(
1357 'bundle: unexpected changegroup version %s' % cgversion)
1358 'bundle: unexpected changegroup version %s' % cgversion)
1358
1359
1359 # TODO compression options should be derived from bundlespec parsing.
1360 # TODO compression options should be derived from bundlespec parsing.
1360 # This is a temporary hack to allow adjusting bundle compression
1361 # This is a temporary hack to allow adjusting bundle compression
1361 # level without a) formalizing the bundlespec changes to declare it
1362 # level without a) formalizing the bundlespec changes to declare it
1362 # b) introducing a command flag.
1363 # b) introducing a command flag.
1363 compopts = {}
1364 compopts = {}
1364 complevel = ui.configint('experimental',
1365 complevel = ui.configint('experimental',
1365 'bundlecomplevel.' + bundlespec.compression)
1366 'bundlecomplevel.' + bundlespec.compression)
1366 if complevel is None:
1367 if complevel is None:
1367 complevel = ui.configint('experimental', 'bundlecomplevel')
1368 complevel = ui.configint('experimental', 'bundlecomplevel')
1368 if complevel is not None:
1369 if complevel is not None:
1369 compopts['level'] = complevel
1370 compopts['level'] = complevel
1370
1371
1371 # Allow overriding the bundling of obsmarker in phases through
1372 # Allow overriding the bundling of obsmarker in phases through
1372 # configuration while we don't have a bundle version that include them
1373 # configuration while we don't have a bundle version that include them
1373 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1374 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1374 bundlespec.contentopts['obsolescence'] = True
1375 bundlespec.contentopts['obsolescence'] = True
1375 if repo.ui.configbool('experimental', 'bundle-phases'):
1376 if repo.ui.configbool('experimental', 'bundle-phases'):
1376 bundlespec.contentopts['phases'] = True
1377 bundlespec.contentopts['phases'] = True
1377
1378
1378 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1379 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1379 bundlespec.contentopts, compression=bcompression,
1380 bundlespec.contentopts, compression=bcompression,
1380 compopts=compopts)
1381 compopts=compopts)
1381
1382
1382 @command('cat',
1383 @command('cat',
1383 [('o', 'output', '',
1384 [('o', 'output', '',
1384 _('print output to file with formatted name'), _('FORMAT')),
1385 _('print output to file with formatted name'), _('FORMAT')),
1385 ('r', 'rev', '', _('print the given revision'), _('REV')),
1386 ('r', 'rev', '', _('print the given revision'), _('REV')),
1386 ('', 'decode', None, _('apply any matching decode filter')),
1387 ('', 'decode', None, _('apply any matching decode filter')),
1387 ] + walkopts + formatteropts,
1388 ] + walkopts + formatteropts,
1388 _('[OPTION]... FILE...'),
1389 _('[OPTION]... FILE...'),
1389 helpcategory=command.CATEGORY_FILE_CONTENTS,
1390 helpcategory=command.CATEGORY_FILE_CONTENTS,
1390 inferrepo=True,
1391 inferrepo=True,
1391 intents={INTENT_READONLY})
1392 intents={INTENT_READONLY})
1392 def cat(ui, repo, file1, *pats, **opts):
1393 def cat(ui, repo, file1, *pats, **opts):
1393 """output the current or given revision of files
1394 """output the current or given revision of files
1394
1395
1395 Print the specified files as they were at the given revision. If
1396 Print the specified files as they were at the given revision. If
1396 no revision is given, the parent of the working directory is used.
1397 no revision is given, the parent of the working directory is used.
1397
1398
1398 Output may be to a file, in which case the name of the file is
1399 Output may be to a file, in which case the name of the file is
1399 given using a template string. See :hg:`help templates`. In addition
1400 given using a template string. See :hg:`help templates`. In addition
1400 to the common template keywords, the following formatting rules are
1401 to the common template keywords, the following formatting rules are
1401 supported:
1402 supported:
1402
1403
1403 :``%%``: literal "%" character
1404 :``%%``: literal "%" character
1404 :``%s``: basename of file being printed
1405 :``%s``: basename of file being printed
1405 :``%d``: dirname of file being printed, or '.' if in repository root
1406 :``%d``: dirname of file being printed, or '.' if in repository root
1406 :``%p``: root-relative path name of file being printed
1407 :``%p``: root-relative path name of file being printed
1407 :``%H``: changeset hash (40 hexadecimal digits)
1408 :``%H``: changeset hash (40 hexadecimal digits)
1408 :``%R``: changeset revision number
1409 :``%R``: changeset revision number
1409 :``%h``: short-form changeset hash (12 hexadecimal digits)
1410 :``%h``: short-form changeset hash (12 hexadecimal digits)
1410 :``%r``: zero-padded changeset revision number
1411 :``%r``: zero-padded changeset revision number
1411 :``%b``: basename of the exporting repository
1412 :``%b``: basename of the exporting repository
1412 :``\\``: literal "\\" character
1413 :``\\``: literal "\\" character
1413
1414
1414 .. container:: verbose
1415 .. container:: verbose
1415
1416
1416 Template:
1417 Template:
1417
1418
1418 The following keywords are supported in addition to the common template
1419 The following keywords are supported in addition to the common template
1419 keywords and functions. See also :hg:`help templates`.
1420 keywords and functions. See also :hg:`help templates`.
1420
1421
1421 :data: String. File content.
1422 :data: String. File content.
1422 :path: String. Repository-absolute path of the file.
1423 :path: String. Repository-absolute path of the file.
1423
1424
1424 Returns 0 on success.
1425 Returns 0 on success.
1425 """
1426 """
1426 opts = pycompat.byteskwargs(opts)
1427 opts = pycompat.byteskwargs(opts)
1427 rev = opts.get('rev')
1428 rev = opts.get('rev')
1428 if rev:
1429 if rev:
1429 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1430 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1430 ctx = scmutil.revsingle(repo, rev)
1431 ctx = scmutil.revsingle(repo, rev)
1431 m = scmutil.match(ctx, (file1,) + pats, opts)
1432 m = scmutil.match(ctx, (file1,) + pats, opts)
1432 fntemplate = opts.pop('output', '')
1433 fntemplate = opts.pop('output', '')
1433 if cmdutil.isstdiofilename(fntemplate):
1434 if cmdutil.isstdiofilename(fntemplate):
1434 fntemplate = ''
1435 fntemplate = ''
1435
1436
1436 if fntemplate:
1437 if fntemplate:
1437 fm = formatter.nullformatter(ui, 'cat', opts)
1438 fm = formatter.nullformatter(ui, 'cat', opts)
1438 else:
1439 else:
1439 ui.pager('cat')
1440 ui.pager('cat')
1440 fm = ui.formatter('cat', opts)
1441 fm = ui.formatter('cat', opts)
1441 with fm:
1442 with fm:
1442 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1443 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1443 **pycompat.strkwargs(opts))
1444 **pycompat.strkwargs(opts))
1444
1445
1445 @command('clone',
1446 @command('clone',
1446 [('U', 'noupdate', None, _('the clone will include an empty working '
1447 [('U', 'noupdate', None, _('the clone will include an empty working '
1447 'directory (only a repository)')),
1448 'directory (only a repository)')),
1448 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1449 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1449 _('REV')),
1450 _('REV')),
1450 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1451 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1451 ' and its ancestors'), _('REV')),
1452 ' and its ancestors'), _('REV')),
1452 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1453 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1453 ' changesets and their ancestors'), _('BRANCH')),
1454 ' changesets and their ancestors'), _('BRANCH')),
1454 ('', 'pull', None, _('use pull protocol to copy metadata')),
1455 ('', 'pull', None, _('use pull protocol to copy metadata')),
1455 ('', 'uncompressed', None,
1456 ('', 'uncompressed', None,
1456 _('an alias to --stream (DEPRECATED)')),
1457 _('an alias to --stream (DEPRECATED)')),
1457 ('', 'stream', None,
1458 ('', 'stream', None,
1458 _('clone with minimal data processing')),
1459 _('clone with minimal data processing')),
1459 ] + remoteopts,
1460 ] + remoteopts,
1460 _('[OPTION]... SOURCE [DEST]'),
1461 _('[OPTION]... SOURCE [DEST]'),
1461 helpcategory=command.CATEGORY_REPO_CREATION,
1462 helpcategory=command.CATEGORY_REPO_CREATION,
1462 helpbasic=True, norepo=True)
1463 helpbasic=True, norepo=True)
1463 def clone(ui, source, dest=None, **opts):
1464 def clone(ui, source, dest=None, **opts):
1464 """make a copy of an existing repository
1465 """make a copy of an existing repository
1465
1466
1466 Create a copy of an existing repository in a new directory.
1467 Create a copy of an existing repository in a new directory.
1467
1468
1468 If no destination directory name is specified, it defaults to the
1469 If no destination directory name is specified, it defaults to the
1469 basename of the source.
1470 basename of the source.
1470
1471
1471 The location of the source is added to the new repository's
1472 The location of the source is added to the new repository's
1472 ``.hg/hgrc`` file, as the default to be used for future pulls.
1473 ``.hg/hgrc`` file, as the default to be used for future pulls.
1473
1474
1474 Only local paths and ``ssh://`` URLs are supported as
1475 Only local paths and ``ssh://`` URLs are supported as
1475 destinations. For ``ssh://`` destinations, no working directory or
1476 destinations. For ``ssh://`` destinations, no working directory or
1476 ``.hg/hgrc`` will be created on the remote side.
1477 ``.hg/hgrc`` will be created on the remote side.
1477
1478
1478 If the source repository has a bookmark called '@' set, that
1479 If the source repository has a bookmark called '@' set, that
1479 revision will be checked out in the new repository by default.
1480 revision will be checked out in the new repository by default.
1480
1481
1481 To check out a particular version, use -u/--update, or
1482 To check out a particular version, use -u/--update, or
1482 -U/--noupdate to create a clone with no working directory.
1483 -U/--noupdate to create a clone with no working directory.
1483
1484
1484 To pull only a subset of changesets, specify one or more revisions
1485 To pull only a subset of changesets, specify one or more revisions
1485 identifiers with -r/--rev or branches with -b/--branch. The
1486 identifiers with -r/--rev or branches with -b/--branch. The
1486 resulting clone will contain only the specified changesets and
1487 resulting clone will contain only the specified changesets and
1487 their ancestors. These options (or 'clone src#rev dest') imply
1488 their ancestors. These options (or 'clone src#rev dest') imply
1488 --pull, even for local source repositories.
1489 --pull, even for local source repositories.
1489
1490
1490 In normal clone mode, the remote normalizes repository data into a common
1491 In normal clone mode, the remote normalizes repository data into a common
1491 exchange format and the receiving end translates this data into its local
1492 exchange format and the receiving end translates this data into its local
1492 storage format. --stream activates a different clone mode that essentially
1493 storage format. --stream activates a different clone mode that essentially
1493 copies repository files from the remote with minimal data processing. This
1494 copies repository files from the remote with minimal data processing. This
1494 significantly reduces the CPU cost of a clone both remotely and locally.
1495 significantly reduces the CPU cost of a clone both remotely and locally.
1495 However, it often increases the transferred data size by 30-40%. This can
1496 However, it often increases the transferred data size by 30-40%. This can
1496 result in substantially faster clones where I/O throughput is plentiful,
1497 result in substantially faster clones where I/O throughput is plentiful,
1497 especially for larger repositories. A side-effect of --stream clones is
1498 especially for larger repositories. A side-effect of --stream clones is
1498 that storage settings and requirements on the remote are applied locally:
1499 that storage settings and requirements on the remote are applied locally:
1499 a modern client may inherit legacy or inefficient storage used by the
1500 a modern client may inherit legacy or inefficient storage used by the
1500 remote or a legacy Mercurial client may not be able to clone from a
1501 remote or a legacy Mercurial client may not be able to clone from a
1501 modern Mercurial remote.
1502 modern Mercurial remote.
1502
1503
1503 .. note::
1504 .. note::
1504
1505
1505 Specifying a tag will include the tagged changeset but not the
1506 Specifying a tag will include the tagged changeset but not the
1506 changeset containing the tag.
1507 changeset containing the tag.
1507
1508
1508 .. container:: verbose
1509 .. container:: verbose
1509
1510
1510 For efficiency, hardlinks are used for cloning whenever the
1511 For efficiency, hardlinks are used for cloning whenever the
1511 source and destination are on the same filesystem (note this
1512 source and destination are on the same filesystem (note this
1512 applies only to the repository data, not to the working
1513 applies only to the repository data, not to the working
1513 directory). Some filesystems, such as AFS, implement hardlinking
1514 directory). Some filesystems, such as AFS, implement hardlinking
1514 incorrectly, but do not report errors. In these cases, use the
1515 incorrectly, but do not report errors. In these cases, use the
1515 --pull option to avoid hardlinking.
1516 --pull option to avoid hardlinking.
1516
1517
1517 Mercurial will update the working directory to the first applicable
1518 Mercurial will update the working directory to the first applicable
1518 revision from this list:
1519 revision from this list:
1519
1520
1520 a) null if -U or the source repository has no changesets
1521 a) null if -U or the source repository has no changesets
1521 b) if -u . and the source repository is local, the first parent of
1522 b) if -u . and the source repository is local, the first parent of
1522 the source repository's working directory
1523 the source repository's working directory
1523 c) the changeset specified with -u (if a branch name, this means the
1524 c) the changeset specified with -u (if a branch name, this means the
1524 latest head of that branch)
1525 latest head of that branch)
1525 d) the changeset specified with -r
1526 d) the changeset specified with -r
1526 e) the tipmost head specified with -b
1527 e) the tipmost head specified with -b
1527 f) the tipmost head specified with the url#branch source syntax
1528 f) the tipmost head specified with the url#branch source syntax
1528 g) the revision marked with the '@' bookmark, if present
1529 g) the revision marked with the '@' bookmark, if present
1529 h) the tipmost head of the default branch
1530 h) the tipmost head of the default branch
1530 i) tip
1531 i) tip
1531
1532
1532 When cloning from servers that support it, Mercurial may fetch
1533 When cloning from servers that support it, Mercurial may fetch
1533 pre-generated data from a server-advertised URL or inline from the
1534 pre-generated data from a server-advertised URL or inline from the
1534 same stream. When this is done, hooks operating on incoming changesets
1535 same stream. When this is done, hooks operating on incoming changesets
1535 and changegroups may fire more than once, once for each pre-generated
1536 and changegroups may fire more than once, once for each pre-generated
1536 bundle and as well as for any additional remaining data. In addition,
1537 bundle and as well as for any additional remaining data. In addition,
1537 if an error occurs, the repository may be rolled back to a partial
1538 if an error occurs, the repository may be rolled back to a partial
1538 clone. This behavior may change in future releases.
1539 clone. This behavior may change in future releases.
1539 See :hg:`help -e clonebundles` for more.
1540 See :hg:`help -e clonebundles` for more.
1540
1541
1541 Examples:
1542 Examples:
1542
1543
1543 - clone a remote repository to a new directory named hg/::
1544 - clone a remote repository to a new directory named hg/::
1544
1545
1545 hg clone https://www.mercurial-scm.org/repo/hg/
1546 hg clone https://www.mercurial-scm.org/repo/hg/
1546
1547
1547 - create a lightweight local clone::
1548 - create a lightweight local clone::
1548
1549
1549 hg clone project/ project-feature/
1550 hg clone project/ project-feature/
1550
1551
1551 - clone from an absolute path on an ssh server (note double-slash)::
1552 - clone from an absolute path on an ssh server (note double-slash)::
1552
1553
1553 hg clone ssh://user@server//home/projects/alpha/
1554 hg clone ssh://user@server//home/projects/alpha/
1554
1555
1555 - do a streaming clone while checking out a specified version::
1556 - do a streaming clone while checking out a specified version::
1556
1557
1557 hg clone --stream http://server/repo -u 1.5
1558 hg clone --stream http://server/repo -u 1.5
1558
1559
1559 - create a repository without changesets after a particular revision::
1560 - create a repository without changesets after a particular revision::
1560
1561
1561 hg clone -r 04e544 experimental/ good/
1562 hg clone -r 04e544 experimental/ good/
1562
1563
1563 - clone (and track) a particular named branch::
1564 - clone (and track) a particular named branch::
1564
1565
1565 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1566 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1566
1567
1567 See :hg:`help urls` for details on specifying URLs.
1568 See :hg:`help urls` for details on specifying URLs.
1568
1569
1569 Returns 0 on success.
1570 Returns 0 on success.
1570 """
1571 """
1571 opts = pycompat.byteskwargs(opts)
1572 opts = pycompat.byteskwargs(opts)
1572 if opts.get('noupdate') and opts.get('updaterev'):
1573 if opts.get('noupdate') and opts.get('updaterev'):
1573 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1574 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1574
1575
1575 # --include/--exclude can come from narrow or sparse.
1576 # --include/--exclude can come from narrow or sparse.
1576 includepats, excludepats = None, None
1577 includepats, excludepats = None, None
1577
1578
1578 # hg.clone() differentiates between None and an empty set. So make sure
1579 # hg.clone() differentiates between None and an empty set. So make sure
1579 # patterns are sets if narrow is requested without patterns.
1580 # patterns are sets if narrow is requested without patterns.
1580 if opts.get('narrow'):
1581 if opts.get('narrow'):
1581 includepats = set()
1582 includepats = set()
1582 excludepats = set()
1583 excludepats = set()
1583
1584
1584 if opts.get('include'):
1585 if opts.get('include'):
1585 includepats = narrowspec.parsepatterns(opts.get('include'))
1586 includepats = narrowspec.parsepatterns(opts.get('include'))
1586 if opts.get('exclude'):
1587 if opts.get('exclude'):
1587 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1588 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1588
1589
1589 r = hg.clone(ui, opts, source, dest,
1590 r = hg.clone(ui, opts, source, dest,
1590 pull=opts.get('pull'),
1591 pull=opts.get('pull'),
1591 stream=opts.get('stream') or opts.get('uncompressed'),
1592 stream=opts.get('stream') or opts.get('uncompressed'),
1592 revs=opts.get('rev'),
1593 revs=opts.get('rev'),
1593 update=opts.get('updaterev') or not opts.get('noupdate'),
1594 update=opts.get('updaterev') or not opts.get('noupdate'),
1594 branch=opts.get('branch'),
1595 branch=opts.get('branch'),
1595 shareopts=opts.get('shareopts'),
1596 shareopts=opts.get('shareopts'),
1596 storeincludepats=includepats,
1597 storeincludepats=includepats,
1597 storeexcludepats=excludepats,
1598 storeexcludepats=excludepats,
1598 depth=opts.get('depth') or None)
1599 depth=opts.get('depth') or None)
1599
1600
1600 return r is None
1601 return r is None
1601
1602
1602 @command('commit|ci',
1603 @command('commit|ci',
1603 [('A', 'addremove', None,
1604 [('A', 'addremove', None,
1604 _('mark new/missing files as added/removed before committing')),
1605 _('mark new/missing files as added/removed before committing')),
1605 ('', 'close-branch', None,
1606 ('', 'close-branch', None,
1606 _('mark a branch head as closed')),
1607 _('mark a branch head as closed')),
1607 ('', 'amend', None, _('amend the parent of the working directory')),
1608 ('', 'amend', None, _('amend the parent of the working directory')),
1608 ('s', 'secret', None, _('use the secret phase for committing')),
1609 ('s', 'secret', None, _('use the secret phase for committing')),
1609 ('e', 'edit', None, _('invoke editor on commit messages')),
1610 ('e', 'edit', None, _('invoke editor on commit messages')),
1610 ('', 'force-close-branch', None,
1611 ('', 'force-close-branch', None,
1611 _('forcibly close branch from a non-head changeset (ADVANCED)')),
1612 _('forcibly close branch from a non-head changeset (ADVANCED)')),
1612 ('i', 'interactive', None, _('use interactive mode')),
1613 ('i', 'interactive', None, _('use interactive mode')),
1613 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1614 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1614 _('[OPTION]... [FILE]...'),
1615 _('[OPTION]... [FILE]...'),
1615 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1616 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1616 inferrepo=True)
1617 inferrepo=True)
1617 def commit(ui, repo, *pats, **opts):
1618 def commit(ui, repo, *pats, **opts):
1618 """commit the specified files or all outstanding changes
1619 """commit the specified files or all outstanding changes
1619
1620
1620 Commit changes to the given files into the repository. Unlike a
1621 Commit changes to the given files into the repository. Unlike a
1621 centralized SCM, this operation is a local operation. See
1622 centralized SCM, this operation is a local operation. See
1622 :hg:`push` for a way to actively distribute your changes.
1623 :hg:`push` for a way to actively distribute your changes.
1623
1624
1624 If a list of files is omitted, all changes reported by :hg:`status`
1625 If a list of files is omitted, all changes reported by :hg:`status`
1625 will be committed.
1626 will be committed.
1626
1627
1627 If you are committing the result of a merge, do not provide any
1628 If you are committing the result of a merge, do not provide any
1628 filenames or -I/-X filters.
1629 filenames or -I/-X filters.
1629
1630
1630 If no commit message is specified, Mercurial starts your
1631 If no commit message is specified, Mercurial starts your
1631 configured editor where you can enter a message. In case your
1632 configured editor where you can enter a message. In case your
1632 commit fails, you will find a backup of your message in
1633 commit fails, you will find a backup of your message in
1633 ``.hg/last-message.txt``.
1634 ``.hg/last-message.txt``.
1634
1635
1635 The --close-branch flag can be used to mark the current branch
1636 The --close-branch flag can be used to mark the current branch
1636 head closed. When all heads of a branch are closed, the branch
1637 head closed. When all heads of a branch are closed, the branch
1637 will be considered closed and no longer listed.
1638 will be considered closed and no longer listed.
1638
1639
1639 The --amend flag can be used to amend the parent of the
1640 The --amend flag can be used to amend the parent of the
1640 working directory with a new commit that contains the changes
1641 working directory with a new commit that contains the changes
1641 in the parent in addition to those currently reported by :hg:`status`,
1642 in the parent in addition to those currently reported by :hg:`status`,
1642 if there are any. The old commit is stored in a backup bundle in
1643 if there are any. The old commit is stored in a backup bundle in
1643 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1644 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1644 on how to restore it).
1645 on how to restore it).
1645
1646
1646 Message, user and date are taken from the amended commit unless
1647 Message, user and date are taken from the amended commit unless
1647 specified. When a message isn't specified on the command line,
1648 specified. When a message isn't specified on the command line,
1648 the editor will open with the message of the amended commit.
1649 the editor will open with the message of the amended commit.
1649
1650
1650 It is not possible to amend public changesets (see :hg:`help phases`)
1651 It is not possible to amend public changesets (see :hg:`help phases`)
1651 or changesets that have children.
1652 or changesets that have children.
1652
1653
1653 See :hg:`help dates` for a list of formats valid for -d/--date.
1654 See :hg:`help dates` for a list of formats valid for -d/--date.
1654
1655
1655 Returns 0 on success, 1 if nothing changed.
1656 Returns 0 on success, 1 if nothing changed.
1656
1657
1657 .. container:: verbose
1658 .. container:: verbose
1658
1659
1659 Examples:
1660 Examples:
1660
1661
1661 - commit all files ending in .py::
1662 - commit all files ending in .py::
1662
1663
1663 hg commit --include "set:**.py"
1664 hg commit --include "set:**.py"
1664
1665
1665 - commit all non-binary files::
1666 - commit all non-binary files::
1666
1667
1667 hg commit --exclude "set:binary()"
1668 hg commit --exclude "set:binary()"
1668
1669
1669 - amend the current commit and set the date to now::
1670 - amend the current commit and set the date to now::
1670
1671
1671 hg commit --amend --date now
1672 hg commit --amend --date now
1672 """
1673 """
1673 with repo.wlock(), repo.lock():
1674 with repo.wlock(), repo.lock():
1674 return _docommit(ui, repo, *pats, **opts)
1675 return _docommit(ui, repo, *pats, **opts)
1675
1676
1676 def _docommit(ui, repo, *pats, **opts):
1677 def _docommit(ui, repo, *pats, **opts):
1677 if opts.get(r'interactive'):
1678 if opts.get(r'interactive'):
1678 opts.pop(r'interactive')
1679 opts.pop(r'interactive')
1679 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1680 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1680 cmdutil.recordfilter, *pats,
1681 cmdutil.recordfilter, *pats,
1681 **opts)
1682 **opts)
1682 # ret can be 0 (no changes to record) or the value returned by
1683 # ret can be 0 (no changes to record) or the value returned by
1683 # commit(), 1 if nothing changed or None on success.
1684 # commit(), 1 if nothing changed or None on success.
1684 return 1 if ret == 0 else ret
1685 return 1 if ret == 0 else ret
1685
1686
1686 opts = pycompat.byteskwargs(opts)
1687 opts = pycompat.byteskwargs(opts)
1687 if opts.get('subrepos'):
1688 if opts.get('subrepos'):
1688 if opts.get('amend'):
1689 if opts.get('amend'):
1689 raise error.Abort(_('cannot amend with --subrepos'))
1690 raise error.Abort(_('cannot amend with --subrepos'))
1690 # Let --subrepos on the command line override config setting.
1691 # Let --subrepos on the command line override config setting.
1691 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1692 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1692
1693
1693 cmdutil.checkunfinished(repo, commit=True)
1694 cmdutil.checkunfinished(repo, commit=True)
1694
1695
1695 branch = repo[None].branch()
1696 branch = repo[None].branch()
1696 bheads = repo.branchheads(branch)
1697 bheads = repo.branchheads(branch)
1697
1698
1698 extra = {}
1699 extra = {}
1699 if opts.get('close_branch') or opts.get('force_close_branch'):
1700 if opts.get('close_branch') or opts.get('force_close_branch'):
1700 extra['close'] = '1'
1701 extra['close'] = '1'
1701
1702
1702 if repo['.'].closesbranch():
1703 if repo['.'].closesbranch():
1703 raise error.Abort(_('current revision is already a branch closing'
1704 raise error.Abort(_('current revision is already a branch closing'
1704 ' head'))
1705 ' head'))
1705 elif not bheads:
1706 elif not bheads:
1706 raise error.Abort(_('branch "%s" has no heads to close') % branch)
1707 raise error.Abort(_('branch "%s" has no heads to close') % branch)
1707 elif (branch == repo['.'].branch() and repo['.'].node() not in bheads
1708 elif (branch == repo['.'].branch() and repo['.'].node() not in bheads
1708 and not opts.get('force_close_branch')):
1709 and not opts.get('force_close_branch')):
1709 hint = _('use --force-close-branch to close branch from a non-head'
1710 hint = _('use --force-close-branch to close branch from a non-head'
1710 ' changeset')
1711 ' changeset')
1711 raise error.Abort(_('can only close branch heads'), hint=hint)
1712 raise error.Abort(_('can only close branch heads'), hint=hint)
1712 elif opts.get('amend'):
1713 elif opts.get('amend'):
1713 if (repo['.'].p1().branch() != branch and
1714 if (repo['.'].p1().branch() != branch and
1714 repo['.'].p2().branch() != branch):
1715 repo['.'].p2().branch() != branch):
1715 raise error.Abort(_('can only close branch heads'))
1716 raise error.Abort(_('can only close branch heads'))
1716
1717
1717 if opts.get('amend'):
1718 if opts.get('amend'):
1718 if ui.configbool('ui', 'commitsubrepos'):
1719 if ui.configbool('ui', 'commitsubrepos'):
1719 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1720 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1720
1721
1721 old = repo['.']
1722 old = repo['.']
1722 rewriteutil.precheck(repo, [old.rev()], 'amend')
1723 rewriteutil.precheck(repo, [old.rev()], 'amend')
1723
1724
1724 # Currently histedit gets confused if an amend happens while histedit
1725 # Currently histedit gets confused if an amend happens while histedit
1725 # is in progress. Since we have a checkunfinished command, we are
1726 # is in progress. Since we have a checkunfinished command, we are
1726 # temporarily honoring it.
1727 # temporarily honoring it.
1727 #
1728 #
1728 # Note: eventually this guard will be removed. Please do not expect
1729 # Note: eventually this guard will be removed. Please do not expect
1729 # this behavior to remain.
1730 # this behavior to remain.
1730 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1731 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1731 cmdutil.checkunfinished(repo)
1732 cmdutil.checkunfinished(repo)
1732
1733
1733 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1734 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1734 if node == old.node():
1735 if node == old.node():
1735 ui.status(_("nothing changed\n"))
1736 ui.status(_("nothing changed\n"))
1736 return 1
1737 return 1
1737 else:
1738 else:
1738 def commitfunc(ui, repo, message, match, opts):
1739 def commitfunc(ui, repo, message, match, opts):
1739 overrides = {}
1740 overrides = {}
1740 if opts.get('secret'):
1741 if opts.get('secret'):
1741 overrides[('phases', 'new-commit')] = 'secret'
1742 overrides[('phases', 'new-commit')] = 'secret'
1742
1743
1743 baseui = repo.baseui
1744 baseui = repo.baseui
1744 with baseui.configoverride(overrides, 'commit'):
1745 with baseui.configoverride(overrides, 'commit'):
1745 with ui.configoverride(overrides, 'commit'):
1746 with ui.configoverride(overrides, 'commit'):
1746 editform = cmdutil.mergeeditform(repo[None],
1747 editform = cmdutil.mergeeditform(repo[None],
1747 'commit.normal')
1748 'commit.normal')
1748 editor = cmdutil.getcommiteditor(
1749 editor = cmdutil.getcommiteditor(
1749 editform=editform, **pycompat.strkwargs(opts))
1750 editform=editform, **pycompat.strkwargs(opts))
1750 return repo.commit(message,
1751 return repo.commit(message,
1751 opts.get('user'),
1752 opts.get('user'),
1752 opts.get('date'),
1753 opts.get('date'),
1753 match,
1754 match,
1754 editor=editor,
1755 editor=editor,
1755 extra=extra)
1756 extra=extra)
1756
1757
1757 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1758 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1758
1759
1759 if not node:
1760 if not node:
1760 stat = cmdutil.postcommitstatus(repo, pats, opts)
1761 stat = cmdutil.postcommitstatus(repo, pats, opts)
1761 if stat[3]:
1762 if stat[3]:
1762 ui.status(_("nothing changed (%d missing files, see "
1763 ui.status(_("nothing changed (%d missing files, see "
1763 "'hg status')\n") % len(stat[3]))
1764 "'hg status')\n") % len(stat[3]))
1764 else:
1765 else:
1765 ui.status(_("nothing changed\n"))
1766 ui.status(_("nothing changed\n"))
1766 return 1
1767 return 1
1767
1768
1768 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1769 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1769
1770
1770 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1771 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1771 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1772 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1772 unknown=True, subrepos=opts.get('subrepos'))
1773 unknown=True, subrepos=opts.get('subrepos'))
1773
1774
1774 @command('config|showconfig|debugconfig',
1775 @command('config|showconfig|debugconfig',
1775 [('u', 'untrusted', None, _('show untrusted configuration options')),
1776 [('u', 'untrusted', None, _('show untrusted configuration options')),
1776 ('e', 'edit', None, _('edit user config')),
1777 ('e', 'edit', None, _('edit user config')),
1777 ('l', 'local', None, _('edit repository config')),
1778 ('l', 'local', None, _('edit repository config')),
1778 ('g', 'global', None, _('edit global config'))] + formatteropts,
1779 ('g', 'global', None, _('edit global config'))] + formatteropts,
1779 _('[-u] [NAME]...'),
1780 _('[-u] [NAME]...'),
1780 helpcategory=command.CATEGORY_HELP,
1781 helpcategory=command.CATEGORY_HELP,
1781 optionalrepo=True,
1782 optionalrepo=True,
1782 intents={INTENT_READONLY})
1783 intents={INTENT_READONLY})
1783 def config(ui, repo, *values, **opts):
1784 def config(ui, repo, *values, **opts):
1784 """show combined config settings from all hgrc files
1785 """show combined config settings from all hgrc files
1785
1786
1786 With no arguments, print names and values of all config items.
1787 With no arguments, print names and values of all config items.
1787
1788
1788 With one argument of the form section.name, print just the value
1789 With one argument of the form section.name, print just the value
1789 of that config item.
1790 of that config item.
1790
1791
1791 With multiple arguments, print names and values of all config
1792 With multiple arguments, print names and values of all config
1792 items with matching section names or section.names.
1793 items with matching section names or section.names.
1793
1794
1794 With --edit, start an editor on the user-level config file. With
1795 With --edit, start an editor on the user-level config file. With
1795 --global, edit the system-wide config file. With --local, edit the
1796 --global, edit the system-wide config file. With --local, edit the
1796 repository-level config file.
1797 repository-level config file.
1797
1798
1798 With --debug, the source (filename and line number) is printed
1799 With --debug, the source (filename and line number) is printed
1799 for each config item.
1800 for each config item.
1800
1801
1801 See :hg:`help config` for more information about config files.
1802 See :hg:`help config` for more information about config files.
1802
1803
1803 .. container:: verbose
1804 .. container:: verbose
1804
1805
1805 Template:
1806 Template:
1806
1807
1807 The following keywords are supported. See also :hg:`help templates`.
1808 The following keywords are supported. See also :hg:`help templates`.
1808
1809
1809 :name: String. Config name.
1810 :name: String. Config name.
1810 :source: String. Filename and line number where the item is defined.
1811 :source: String. Filename and line number where the item is defined.
1811 :value: String. Config value.
1812 :value: String. Config value.
1812
1813
1813 Returns 0 on success, 1 if NAME does not exist.
1814 Returns 0 on success, 1 if NAME does not exist.
1814
1815
1815 """
1816 """
1816
1817
1817 opts = pycompat.byteskwargs(opts)
1818 opts = pycompat.byteskwargs(opts)
1818 if opts.get('edit') or opts.get('local') or opts.get('global'):
1819 if opts.get('edit') or opts.get('local') or opts.get('global'):
1819 if opts.get('local') and opts.get('global'):
1820 if opts.get('local') and opts.get('global'):
1820 raise error.Abort(_("can't use --local and --global together"))
1821 raise error.Abort(_("can't use --local and --global together"))
1821
1822
1822 if opts.get('local'):
1823 if opts.get('local'):
1823 if not repo:
1824 if not repo:
1824 raise error.Abort(_("can't use --local outside a repository"))
1825 raise error.Abort(_("can't use --local outside a repository"))
1825 paths = [repo.vfs.join('hgrc')]
1826 paths = [repo.vfs.join('hgrc')]
1826 elif opts.get('global'):
1827 elif opts.get('global'):
1827 paths = rcutil.systemrcpath()
1828 paths = rcutil.systemrcpath()
1828 else:
1829 else:
1829 paths = rcutil.userrcpath()
1830 paths = rcutil.userrcpath()
1830
1831
1831 for f in paths:
1832 for f in paths:
1832 if os.path.exists(f):
1833 if os.path.exists(f):
1833 break
1834 break
1834 else:
1835 else:
1835 if opts.get('global'):
1836 if opts.get('global'):
1836 samplehgrc = uimod.samplehgrcs['global']
1837 samplehgrc = uimod.samplehgrcs['global']
1837 elif opts.get('local'):
1838 elif opts.get('local'):
1838 samplehgrc = uimod.samplehgrcs['local']
1839 samplehgrc = uimod.samplehgrcs['local']
1839 else:
1840 else:
1840 samplehgrc = uimod.samplehgrcs['user']
1841 samplehgrc = uimod.samplehgrcs['user']
1841
1842
1842 f = paths[0]
1843 f = paths[0]
1843 fp = open(f, "wb")
1844 fp = open(f, "wb")
1844 fp.write(util.tonativeeol(samplehgrc))
1845 fp.write(util.tonativeeol(samplehgrc))
1845 fp.close()
1846 fp.close()
1846
1847
1847 editor = ui.geteditor()
1848 editor = ui.geteditor()
1848 ui.system("%s \"%s\"" % (editor, f),
1849 ui.system("%s \"%s\"" % (editor, f),
1849 onerr=error.Abort, errprefix=_("edit failed"),
1850 onerr=error.Abort, errprefix=_("edit failed"),
1850 blockedtag='config_edit')
1851 blockedtag='config_edit')
1851 return
1852 return
1852 ui.pager('config')
1853 ui.pager('config')
1853 fm = ui.formatter('config', opts)
1854 fm = ui.formatter('config', opts)
1854 for t, f in rcutil.rccomponents():
1855 for t, f in rcutil.rccomponents():
1855 if t == 'path':
1856 if t == 'path':
1856 ui.debug('read config from: %s\n' % f)
1857 ui.debug('read config from: %s\n' % f)
1857 elif t == 'items':
1858 elif t == 'items':
1858 for section, name, value, source in f:
1859 for section, name, value, source in f:
1859 ui.debug('set config by: %s\n' % source)
1860 ui.debug('set config by: %s\n' % source)
1860 else:
1861 else:
1861 raise error.ProgrammingError('unknown rctype: %s' % t)
1862 raise error.ProgrammingError('unknown rctype: %s' % t)
1862 untrusted = bool(opts.get('untrusted'))
1863 untrusted = bool(opts.get('untrusted'))
1863
1864
1864 selsections = selentries = []
1865 selsections = selentries = []
1865 if values:
1866 if values:
1866 selsections = [v for v in values if '.' not in v]
1867 selsections = [v for v in values if '.' not in v]
1867 selentries = [v for v in values if '.' in v]
1868 selentries = [v for v in values if '.' in v]
1868 uniquesel = (len(selentries) == 1 and not selsections)
1869 uniquesel = (len(selentries) == 1 and not selsections)
1869 selsections = set(selsections)
1870 selsections = set(selsections)
1870 selentries = set(selentries)
1871 selentries = set(selentries)
1871
1872
1872 matched = False
1873 matched = False
1873 for section, name, value in ui.walkconfig(untrusted=untrusted):
1874 for section, name, value in ui.walkconfig(untrusted=untrusted):
1874 source = ui.configsource(section, name, untrusted)
1875 source = ui.configsource(section, name, untrusted)
1875 value = pycompat.bytestr(value)
1876 value = pycompat.bytestr(value)
1876 defaultvalue = ui.configdefault(section, name)
1877 defaultvalue = ui.configdefault(section, name)
1877 if fm.isplain():
1878 if fm.isplain():
1878 source = source or 'none'
1879 source = source or 'none'
1879 value = value.replace('\n', '\\n')
1880 value = value.replace('\n', '\\n')
1880 entryname = section + '.' + name
1881 entryname = section + '.' + name
1881 if values and not (section in selsections or entryname in selentries):
1882 if values and not (section in selsections or entryname in selentries):
1882 continue
1883 continue
1883 fm.startitem()
1884 fm.startitem()
1884 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1885 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1885 if uniquesel:
1886 if uniquesel:
1886 fm.data(name=entryname)
1887 fm.data(name=entryname)
1887 fm.write('value', '%s\n', value)
1888 fm.write('value', '%s\n', value)
1888 else:
1889 else:
1889 fm.write('name value', '%s=%s\n', entryname, value)
1890 fm.write('name value', '%s=%s\n', entryname, value)
1890 fm.data(defaultvalue=defaultvalue)
1891 fm.data(defaultvalue=defaultvalue)
1891 matched = True
1892 matched = True
1892 fm.end()
1893 fm.end()
1893 if matched:
1894 if matched:
1894 return 0
1895 return 0
1895 return 1
1896 return 1
1896
1897
1897 @command('continue',
1898 @command('continue',
1898 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1899 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1899 helpbasic=True)
1900 helpbasic=True)
1900 def continuecmd(ui, repo, **opts):
1901 def continuecmd(ui, repo, **opts):
1901 """resumes an interrupted operation (EXPERIMENTAL)
1902 """resumes an interrupted operation (EXPERIMENTAL)
1902
1903
1903 Finishes a multistep operation like graft, histedit, rebase, merge,
1904 Finishes a multistep operation like graft, histedit, rebase, merge,
1904 and unshelve if they are in an interrupted state.
1905 and unshelve if they are in an interrupted state.
1905
1906
1906 use --dry-run/-n to dry run the command.
1907 use --dry-run/-n to dry run the command.
1907 """
1908 """
1908 dryrun = opts.get(r'dry_run')
1909 dryrun = opts.get(r'dry_run')
1909 contstate = cmdutil.getunfinishedstate(repo)
1910 contstate = cmdutil.getunfinishedstate(repo)
1910 if not contstate:
1911 if not contstate:
1911 raise error.Abort(_('no operation in progress'))
1912 raise error.Abort(_('no operation in progress'))
1912 if not contstate.continuefunc:
1913 if not contstate.continuefunc:
1913 raise error.Abort((_("%s in progress but does not support "
1914 raise error.Abort((_("%s in progress but does not support "
1914 "'hg continue'") % (contstate._opname)),
1915 "'hg continue'") % (contstate._opname)),
1915 hint=contstate.continuemsg())
1916 hint=contstate.continuemsg())
1916 if dryrun:
1917 if dryrun:
1917 ui.status(_('%s in progress, will be resumed\n') % (contstate._opname))
1918 ui.status(_('%s in progress, will be resumed\n') % (contstate._opname))
1918 return
1919 return
1919 return contstate.continuefunc(ui, repo)
1920 return contstate.continuefunc(ui, repo)
1920
1921
1921 @command('copy|cp',
1922 @command('copy|cp',
1922 [('A', 'after', None, _('record a copy that has already occurred')),
1923 [('A', 'after', None, _('record a copy that has already occurred')),
1923 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1924 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1924 ] + walkopts + dryrunopts,
1925 ] + walkopts + dryrunopts,
1925 _('[OPTION]... SOURCE... DEST'),
1926 _('[OPTION]... SOURCE... DEST'),
1926 helpcategory=command.CATEGORY_FILE_CONTENTS)
1927 helpcategory=command.CATEGORY_FILE_CONTENTS)
1927 def copy(ui, repo, *pats, **opts):
1928 def copy(ui, repo, *pats, **opts):
1928 """mark files as copied for the next commit
1929 """mark files as copied for the next commit
1929
1930
1930 Mark dest as having copies of source files. If dest is a
1931 Mark dest as having copies of source files. If dest is a
1931 directory, copies are put in that directory. If dest is a file,
1932 directory, copies are put in that directory. If dest is a file,
1932 the source must be a single file.
1933 the source must be a single file.
1933
1934
1934 By default, this command copies the contents of files as they
1935 By default, this command copies the contents of files as they
1935 exist in the working directory. If invoked with -A/--after, the
1936 exist in the working directory. If invoked with -A/--after, the
1936 operation is recorded, but no copying is performed.
1937 operation is recorded, but no copying is performed.
1937
1938
1938 This command takes effect with the next commit. To undo a copy
1939 This command takes effect with the next commit. To undo a copy
1939 before that, see :hg:`revert`.
1940 before that, see :hg:`revert`.
1940
1941
1941 Returns 0 on success, 1 if errors are encountered.
1942 Returns 0 on success, 1 if errors are encountered.
1942 """
1943 """
1943 opts = pycompat.byteskwargs(opts)
1944 opts = pycompat.byteskwargs(opts)
1944 with repo.wlock(False):
1945 with repo.wlock(False):
1945 return cmdutil.copy(ui, repo, pats, opts)
1946 return cmdutil.copy(ui, repo, pats, opts)
1946
1947
1947 @command(
1948 @command(
1948 'debugcommands', [], _('[COMMAND]'),
1949 'debugcommands', [], _('[COMMAND]'),
1949 helpcategory=command.CATEGORY_HELP,
1950 helpcategory=command.CATEGORY_HELP,
1950 norepo=True)
1951 norepo=True)
1951 def debugcommands(ui, cmd='', *args):
1952 def debugcommands(ui, cmd='', *args):
1952 """list all available commands and options"""
1953 """list all available commands and options"""
1953 for cmd, vals in sorted(table.iteritems()):
1954 for cmd, vals in sorted(table.iteritems()):
1954 cmd = cmd.split('|')[0]
1955 cmd = cmd.split('|')[0]
1955 opts = ', '.join([i[1] for i in vals[1]])
1956 opts = ', '.join([i[1] for i in vals[1]])
1956 ui.write('%s: %s\n' % (cmd, opts))
1957 ui.write('%s: %s\n' % (cmd, opts))
1957
1958
1958 @command('debugcomplete',
1959 @command('debugcomplete',
1959 [('o', 'options', None, _('show the command options'))],
1960 [('o', 'options', None, _('show the command options'))],
1960 _('[-o] CMD'),
1961 _('[-o] CMD'),
1961 helpcategory=command.CATEGORY_HELP,
1962 helpcategory=command.CATEGORY_HELP,
1962 norepo=True)
1963 norepo=True)
1963 def debugcomplete(ui, cmd='', **opts):
1964 def debugcomplete(ui, cmd='', **opts):
1964 """returns the completion list associated with the given command"""
1965 """returns the completion list associated with the given command"""
1965
1966
1966 if opts.get(r'options'):
1967 if opts.get(r'options'):
1967 options = []
1968 options = []
1968 otables = [globalopts]
1969 otables = [globalopts]
1969 if cmd:
1970 if cmd:
1970 aliases, entry = cmdutil.findcmd(cmd, table, False)
1971 aliases, entry = cmdutil.findcmd(cmd, table, False)
1971 otables.append(entry[1])
1972 otables.append(entry[1])
1972 for t in otables:
1973 for t in otables:
1973 for o in t:
1974 for o in t:
1974 if "(DEPRECATED)" in o[3]:
1975 if "(DEPRECATED)" in o[3]:
1975 continue
1976 continue
1976 if o[0]:
1977 if o[0]:
1977 options.append('-%s' % o[0])
1978 options.append('-%s' % o[0])
1978 options.append('--%s' % o[1])
1979 options.append('--%s' % o[1])
1979 ui.write("%s\n" % "\n".join(options))
1980 ui.write("%s\n" % "\n".join(options))
1980 return
1981 return
1981
1982
1982 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1983 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1983 if ui.verbose:
1984 if ui.verbose:
1984 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1985 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1985 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1986 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1986
1987
1987 @command('diff',
1988 @command('diff',
1988 [('r', 'rev', [], _('revision'), _('REV')),
1989 [('r', 'rev', [], _('revision'), _('REV')),
1989 ('c', 'change', '', _('change made by revision'), _('REV'))
1990 ('c', 'change', '', _('change made by revision'), _('REV'))
1990 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1991 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1991 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1992 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1992 helpcategory=command.CATEGORY_FILE_CONTENTS,
1993 helpcategory=command.CATEGORY_FILE_CONTENTS,
1993 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1994 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1994 def diff(ui, repo, *pats, **opts):
1995 def diff(ui, repo, *pats, **opts):
1995 """diff repository (or selected files)
1996 """diff repository (or selected files)
1996
1997
1997 Show differences between revisions for the specified files.
1998 Show differences between revisions for the specified files.
1998
1999
1999 Differences between files are shown using the unified diff format.
2000 Differences between files are shown using the unified diff format.
2000
2001
2001 .. note::
2002 .. note::
2002
2003
2003 :hg:`diff` may generate unexpected results for merges, as it will
2004 :hg:`diff` may generate unexpected results for merges, as it will
2004 default to comparing against the working directory's first
2005 default to comparing against the working directory's first
2005 parent changeset if no revisions are specified.
2006 parent changeset if no revisions are specified.
2006
2007
2007 When two revision arguments are given, then changes are shown
2008 When two revision arguments are given, then changes are shown
2008 between those revisions. If only one revision is specified then
2009 between those revisions. If only one revision is specified then
2009 that revision is compared to the working directory, and, when no
2010 that revision is compared to the working directory, and, when no
2010 revisions are specified, the working directory files are compared
2011 revisions are specified, the working directory files are compared
2011 to its first parent.
2012 to its first parent.
2012
2013
2013 Alternatively you can specify -c/--change with a revision to see
2014 Alternatively you can specify -c/--change with a revision to see
2014 the changes in that changeset relative to its first parent.
2015 the changes in that changeset relative to its first parent.
2015
2016
2016 Without the -a/--text option, diff will avoid generating diffs of
2017 Without the -a/--text option, diff will avoid generating diffs of
2017 files it detects as binary. With -a, diff will generate a diff
2018 files it detects as binary. With -a, diff will generate a diff
2018 anyway, probably with undesirable results.
2019 anyway, probably with undesirable results.
2019
2020
2020 Use the -g/--git option to generate diffs in the git extended diff
2021 Use the -g/--git option to generate diffs in the git extended diff
2021 format. For more information, read :hg:`help diffs`.
2022 format. For more information, read :hg:`help diffs`.
2022
2023
2023 .. container:: verbose
2024 .. container:: verbose
2024
2025
2025 Examples:
2026 Examples:
2026
2027
2027 - compare a file in the current working directory to its parent::
2028 - compare a file in the current working directory to its parent::
2028
2029
2029 hg diff foo.c
2030 hg diff foo.c
2030
2031
2031 - compare two historical versions of a directory, with rename info::
2032 - compare two historical versions of a directory, with rename info::
2032
2033
2033 hg diff --git -r 1.0:1.2 lib/
2034 hg diff --git -r 1.0:1.2 lib/
2034
2035
2035 - get change stats relative to the last change on some date::
2036 - get change stats relative to the last change on some date::
2036
2037
2037 hg diff --stat -r "date('may 2')"
2038 hg diff --stat -r "date('may 2')"
2038
2039
2039 - diff all newly-added files that contain a keyword::
2040 - diff all newly-added files that contain a keyword::
2040
2041
2041 hg diff "set:added() and grep(GNU)"
2042 hg diff "set:added() and grep(GNU)"
2042
2043
2043 - compare a revision and its parents::
2044 - compare a revision and its parents::
2044
2045
2045 hg diff -c 9353 # compare against first parent
2046 hg diff -c 9353 # compare against first parent
2046 hg diff -r 9353^:9353 # same using revset syntax
2047 hg diff -r 9353^:9353 # same using revset syntax
2047 hg diff -r 9353^2:9353 # compare against the second parent
2048 hg diff -r 9353^2:9353 # compare against the second parent
2048
2049
2049 Returns 0 on success.
2050 Returns 0 on success.
2050 """
2051 """
2051
2052
2052 opts = pycompat.byteskwargs(opts)
2053 opts = pycompat.byteskwargs(opts)
2053 revs = opts.get('rev')
2054 revs = opts.get('rev')
2054 change = opts.get('change')
2055 change = opts.get('change')
2055 stat = opts.get('stat')
2056 stat = opts.get('stat')
2056 reverse = opts.get('reverse')
2057 reverse = opts.get('reverse')
2057
2058
2058 if revs and change:
2059 if revs and change:
2059 msg = _('cannot specify --rev and --change at the same time')
2060 msg = _('cannot specify --rev and --change at the same time')
2060 raise error.Abort(msg)
2061 raise error.Abort(msg)
2061 elif change:
2062 elif change:
2062 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2063 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2063 ctx2 = scmutil.revsingle(repo, change, None)
2064 ctx2 = scmutil.revsingle(repo, change, None)
2064 ctx1 = ctx2.p1()
2065 ctx1 = ctx2.p1()
2065 else:
2066 else:
2066 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2067 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2067 ctx1, ctx2 = scmutil.revpair(repo, revs)
2068 ctx1, ctx2 = scmutil.revpair(repo, revs)
2068 node1, node2 = ctx1.node(), ctx2.node()
2069 node1, node2 = ctx1.node(), ctx2.node()
2069
2070
2070 if reverse:
2071 if reverse:
2071 node1, node2 = node2, node1
2072 node1, node2 = node2, node1
2072
2073
2073 diffopts = patch.diffallopts(ui, opts)
2074 diffopts = patch.diffallopts(ui, opts)
2074 m = scmutil.match(ctx2, pats, opts)
2075 m = scmutil.match(ctx2, pats, opts)
2075 m = repo.narrowmatch(m)
2076 m = repo.narrowmatch(m)
2076 ui.pager('diff')
2077 ui.pager('diff')
2077 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2078 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2078 listsubrepos=opts.get('subrepos'),
2079 listsubrepos=opts.get('subrepos'),
2079 root=opts.get('root'))
2080 root=opts.get('root'))
2080
2081
2081 @command('export',
2082 @command('export',
2082 [('B', 'bookmark', '',
2083 [('B', 'bookmark', '',
2083 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2084 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2084 ('o', 'output', '',
2085 ('o', 'output', '',
2085 _('print output to file with formatted name'), _('FORMAT')),
2086 _('print output to file with formatted name'), _('FORMAT')),
2086 ('', 'switch-parent', None, _('diff against the second parent')),
2087 ('', 'switch-parent', None, _('diff against the second parent')),
2087 ('r', 'rev', [], _('revisions to export'), _('REV')),
2088 ('r', 'rev', [], _('revisions to export'), _('REV')),
2088 ] + diffopts + formatteropts,
2089 ] + diffopts + formatteropts,
2089 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2090 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2090 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2091 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2091 helpbasic=True, intents={INTENT_READONLY})
2092 helpbasic=True, intents={INTENT_READONLY})
2092 def export(ui, repo, *changesets, **opts):
2093 def export(ui, repo, *changesets, **opts):
2093 """dump the header and diffs for one or more changesets
2094 """dump the header and diffs for one or more changesets
2094
2095
2095 Print the changeset header and diffs for one or more revisions.
2096 Print the changeset header and diffs for one or more revisions.
2096 If no revision is given, the parent of the working directory is used.
2097 If no revision is given, the parent of the working directory is used.
2097
2098
2098 The information shown in the changeset header is: author, date,
2099 The information shown in the changeset header is: author, date,
2099 branch name (if non-default), changeset hash, parent(s) and commit
2100 branch name (if non-default), changeset hash, parent(s) and commit
2100 comment.
2101 comment.
2101
2102
2102 .. note::
2103 .. note::
2103
2104
2104 :hg:`export` may generate unexpected diff output for merge
2105 :hg:`export` may generate unexpected diff output for merge
2105 changesets, as it will compare the merge changeset against its
2106 changesets, as it will compare the merge changeset against its
2106 first parent only.
2107 first parent only.
2107
2108
2108 Output may be to a file, in which case the name of the file is
2109 Output may be to a file, in which case the name of the file is
2109 given using a template string. See :hg:`help templates`. In addition
2110 given using a template string. See :hg:`help templates`. In addition
2110 to the common template keywords, the following formatting rules are
2111 to the common template keywords, the following formatting rules are
2111 supported:
2112 supported:
2112
2113
2113 :``%%``: literal "%" character
2114 :``%%``: literal "%" character
2114 :``%H``: changeset hash (40 hexadecimal digits)
2115 :``%H``: changeset hash (40 hexadecimal digits)
2115 :``%N``: number of patches being generated
2116 :``%N``: number of patches being generated
2116 :``%R``: changeset revision number
2117 :``%R``: changeset revision number
2117 :``%b``: basename of the exporting repository
2118 :``%b``: basename of the exporting repository
2118 :``%h``: short-form changeset hash (12 hexadecimal digits)
2119 :``%h``: short-form changeset hash (12 hexadecimal digits)
2119 :``%m``: first line of the commit message (only alphanumeric characters)
2120 :``%m``: first line of the commit message (only alphanumeric characters)
2120 :``%n``: zero-padded sequence number, starting at 1
2121 :``%n``: zero-padded sequence number, starting at 1
2121 :``%r``: zero-padded changeset revision number
2122 :``%r``: zero-padded changeset revision number
2122 :``\\``: literal "\\" character
2123 :``\\``: literal "\\" character
2123
2124
2124 Without the -a/--text option, export will avoid generating diffs
2125 Without the -a/--text option, export will avoid generating diffs
2125 of files it detects as binary. With -a, export will generate a
2126 of files it detects as binary. With -a, export will generate a
2126 diff anyway, probably with undesirable results.
2127 diff anyway, probably with undesirable results.
2127
2128
2128 With -B/--bookmark changesets reachable by the given bookmark are
2129 With -B/--bookmark changesets reachable by the given bookmark are
2129 selected.
2130 selected.
2130
2131
2131 Use the -g/--git option to generate diffs in the git extended diff
2132 Use the -g/--git option to generate diffs in the git extended diff
2132 format. See :hg:`help diffs` for more information.
2133 format. See :hg:`help diffs` for more information.
2133
2134
2134 With the --switch-parent option, the diff will be against the
2135 With the --switch-parent option, the diff will be against the
2135 second parent. It can be useful to review a merge.
2136 second parent. It can be useful to review a merge.
2136
2137
2137 .. container:: verbose
2138 .. container:: verbose
2138
2139
2139 Template:
2140 Template:
2140
2141
2141 The following keywords are supported in addition to the common template
2142 The following keywords are supported in addition to the common template
2142 keywords and functions. See also :hg:`help templates`.
2143 keywords and functions. See also :hg:`help templates`.
2143
2144
2144 :diff: String. Diff content.
2145 :diff: String. Diff content.
2145 :parents: List of strings. Parent nodes of the changeset.
2146 :parents: List of strings. Parent nodes of the changeset.
2146
2147
2147 Examples:
2148 Examples:
2148
2149
2149 - use export and import to transplant a bugfix to the current
2150 - use export and import to transplant a bugfix to the current
2150 branch::
2151 branch::
2151
2152
2152 hg export -r 9353 | hg import -
2153 hg export -r 9353 | hg import -
2153
2154
2154 - export all the changesets between two revisions to a file with
2155 - export all the changesets between two revisions to a file with
2155 rename information::
2156 rename information::
2156
2157
2157 hg export --git -r 123:150 > changes.txt
2158 hg export --git -r 123:150 > changes.txt
2158
2159
2159 - split outgoing changes into a series of patches with
2160 - split outgoing changes into a series of patches with
2160 descriptive names::
2161 descriptive names::
2161
2162
2162 hg export -r "outgoing()" -o "%n-%m.patch"
2163 hg export -r "outgoing()" -o "%n-%m.patch"
2163
2164
2164 Returns 0 on success.
2165 Returns 0 on success.
2165 """
2166 """
2166 opts = pycompat.byteskwargs(opts)
2167 opts = pycompat.byteskwargs(opts)
2167 bookmark = opts.get('bookmark')
2168 bookmark = opts.get('bookmark')
2168 changesets += tuple(opts.get('rev', []))
2169 changesets += tuple(opts.get('rev', []))
2169
2170
2170 if bookmark and changesets:
2171 if bookmark and changesets:
2171 raise error.Abort(_("-r and -B are mutually exclusive"))
2172 raise error.Abort(_("-r and -B are mutually exclusive"))
2172
2173
2173 if bookmark:
2174 if bookmark:
2174 if bookmark not in repo._bookmarks:
2175 if bookmark not in repo._bookmarks:
2175 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2176 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2176
2177
2177 revs = scmutil.bookmarkrevs(repo, bookmark)
2178 revs = scmutil.bookmarkrevs(repo, bookmark)
2178 else:
2179 else:
2179 if not changesets:
2180 if not changesets:
2180 changesets = ['.']
2181 changesets = ['.']
2181
2182
2182 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2183 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2183 revs = scmutil.revrange(repo, changesets)
2184 revs = scmutil.revrange(repo, changesets)
2184
2185
2185 if not revs:
2186 if not revs:
2186 raise error.Abort(_("export requires at least one changeset"))
2187 raise error.Abort(_("export requires at least one changeset"))
2187 if len(revs) > 1:
2188 if len(revs) > 1:
2188 ui.note(_('exporting patches:\n'))
2189 ui.note(_('exporting patches:\n'))
2189 else:
2190 else:
2190 ui.note(_('exporting patch:\n'))
2191 ui.note(_('exporting patch:\n'))
2191
2192
2192 fntemplate = opts.get('output')
2193 fntemplate = opts.get('output')
2193 if cmdutil.isstdiofilename(fntemplate):
2194 if cmdutil.isstdiofilename(fntemplate):
2194 fntemplate = ''
2195 fntemplate = ''
2195
2196
2196 if fntemplate:
2197 if fntemplate:
2197 fm = formatter.nullformatter(ui, 'export', opts)
2198 fm = formatter.nullformatter(ui, 'export', opts)
2198 else:
2199 else:
2199 ui.pager('export')
2200 ui.pager('export')
2200 fm = ui.formatter('export', opts)
2201 fm = ui.formatter('export', opts)
2201 with fm:
2202 with fm:
2202 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2203 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2203 switch_parent=opts.get('switch_parent'),
2204 switch_parent=opts.get('switch_parent'),
2204 opts=patch.diffallopts(ui, opts))
2205 opts=patch.diffallopts(ui, opts))
2205
2206
2206 @command('files',
2207 @command('files',
2207 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2208 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2208 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2209 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2209 ] + walkopts + formatteropts + subrepoopts,
2210 ] + walkopts + formatteropts + subrepoopts,
2210 _('[OPTION]... [FILE]...'),
2211 _('[OPTION]... [FILE]...'),
2211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2212 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2212 intents={INTENT_READONLY})
2213 intents={INTENT_READONLY})
2213 def files(ui, repo, *pats, **opts):
2214 def files(ui, repo, *pats, **opts):
2214 """list tracked files
2215 """list tracked files
2215
2216
2216 Print files under Mercurial control in the working directory or
2217 Print files under Mercurial control in the working directory or
2217 specified revision for given files (excluding removed files).
2218 specified revision for given files (excluding removed files).
2218 Files can be specified as filenames or filesets.
2219 Files can be specified as filenames or filesets.
2219
2220
2220 If no files are given to match, this command prints the names
2221 If no files are given to match, this command prints the names
2221 of all files under Mercurial control.
2222 of all files under Mercurial control.
2222
2223
2223 .. container:: verbose
2224 .. container:: verbose
2224
2225
2225 Template:
2226 Template:
2226
2227
2227 The following keywords are supported in addition to the common template
2228 The following keywords are supported in addition to the common template
2228 keywords and functions. See also :hg:`help templates`.
2229 keywords and functions. See also :hg:`help templates`.
2229
2230
2230 :flags: String. Character denoting file's symlink and executable bits.
2231 :flags: String. Character denoting file's symlink and executable bits.
2231 :path: String. Repository-absolute path of the file.
2232 :path: String. Repository-absolute path of the file.
2232 :size: Integer. Size of the file in bytes.
2233 :size: Integer. Size of the file in bytes.
2233
2234
2234 Examples:
2235 Examples:
2235
2236
2236 - list all files under the current directory::
2237 - list all files under the current directory::
2237
2238
2238 hg files .
2239 hg files .
2239
2240
2240 - shows sizes and flags for current revision::
2241 - shows sizes and flags for current revision::
2241
2242
2242 hg files -vr .
2243 hg files -vr .
2243
2244
2244 - list all files named README::
2245 - list all files named README::
2245
2246
2246 hg files -I "**/README"
2247 hg files -I "**/README"
2247
2248
2248 - list all binary files::
2249 - list all binary files::
2249
2250
2250 hg files "set:binary()"
2251 hg files "set:binary()"
2251
2252
2252 - find files containing a regular expression::
2253 - find files containing a regular expression::
2253
2254
2254 hg files "set:grep('bob')"
2255 hg files "set:grep('bob')"
2255
2256
2256 - search tracked file contents with xargs and grep::
2257 - search tracked file contents with xargs and grep::
2257
2258
2258 hg files -0 | xargs -0 grep foo
2259 hg files -0 | xargs -0 grep foo
2259
2260
2260 See :hg:`help patterns` and :hg:`help filesets` for more information
2261 See :hg:`help patterns` and :hg:`help filesets` for more information
2261 on specifying file patterns.
2262 on specifying file patterns.
2262
2263
2263 Returns 0 if a match is found, 1 otherwise.
2264 Returns 0 if a match is found, 1 otherwise.
2264
2265
2265 """
2266 """
2266
2267
2267 opts = pycompat.byteskwargs(opts)
2268 opts = pycompat.byteskwargs(opts)
2268 rev = opts.get('rev')
2269 rev = opts.get('rev')
2269 if rev:
2270 if rev:
2270 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2271 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2271 ctx = scmutil.revsingle(repo, rev, None)
2272 ctx = scmutil.revsingle(repo, rev, None)
2272
2273
2273 end = '\n'
2274 end = '\n'
2274 if opts.get('print0'):
2275 if opts.get('print0'):
2275 end = '\0'
2276 end = '\0'
2276 fmt = '%s' + end
2277 fmt = '%s' + end
2277
2278
2278 m = scmutil.match(ctx, pats, opts)
2279 m = scmutil.match(ctx, pats, opts)
2279 ui.pager('files')
2280 ui.pager('files')
2280 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2281 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2281 with ui.formatter('files', opts) as fm:
2282 with ui.formatter('files', opts) as fm:
2282 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2283 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2283 opts.get('subrepos'))
2284 opts.get('subrepos'))
2284
2285
2285 @command(
2286 @command(
2286 'forget',
2287 'forget',
2287 [('i', 'interactive', None, _('use interactive mode')),
2288 [('i', 'interactive', None, _('use interactive mode')),
2288 ] + walkopts + dryrunopts,
2289 ] + walkopts + dryrunopts,
2289 _('[OPTION]... FILE...'),
2290 _('[OPTION]... FILE...'),
2290 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2291 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2291 helpbasic=True, inferrepo=True)
2292 helpbasic=True, inferrepo=True)
2292 def forget(ui, repo, *pats, **opts):
2293 def forget(ui, repo, *pats, **opts):
2293 """forget the specified files on the next commit
2294 """forget the specified files on the next commit
2294
2295
2295 Mark the specified files so they will no longer be tracked
2296 Mark the specified files so they will no longer be tracked
2296 after the next commit.
2297 after the next commit.
2297
2298
2298 This only removes files from the current branch, not from the
2299 This only removes files from the current branch, not from the
2299 entire project history, and it does not delete them from the
2300 entire project history, and it does not delete them from the
2300 working directory.
2301 working directory.
2301
2302
2302 To delete the file from the working directory, see :hg:`remove`.
2303 To delete the file from the working directory, see :hg:`remove`.
2303
2304
2304 To undo a forget before the next commit, see :hg:`add`.
2305 To undo a forget before the next commit, see :hg:`add`.
2305
2306
2306 .. container:: verbose
2307 .. container:: verbose
2307
2308
2308 Examples:
2309 Examples:
2309
2310
2310 - forget newly-added binary files::
2311 - forget newly-added binary files::
2311
2312
2312 hg forget "set:added() and binary()"
2313 hg forget "set:added() and binary()"
2313
2314
2314 - forget files that would be excluded by .hgignore::
2315 - forget files that would be excluded by .hgignore::
2315
2316
2316 hg forget "set:hgignore()"
2317 hg forget "set:hgignore()"
2317
2318
2318 Returns 0 on success.
2319 Returns 0 on success.
2319 """
2320 """
2320
2321
2321 opts = pycompat.byteskwargs(opts)
2322 opts = pycompat.byteskwargs(opts)
2322 if not pats:
2323 if not pats:
2323 raise error.Abort(_('no files specified'))
2324 raise error.Abort(_('no files specified'))
2324
2325
2325 m = scmutil.match(repo[None], pats, opts)
2326 m = scmutil.match(repo[None], pats, opts)
2326 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2327 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2327 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2328 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2328 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2329 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2329 explicitonly=False, dryrun=dryrun,
2330 explicitonly=False, dryrun=dryrun,
2330 interactive=interactive)[0]
2331 interactive=interactive)[0]
2331 return rejected and 1 or 0
2332 return rejected and 1 or 0
2332
2333
2333 @command(
2334 @command(
2334 'graft',
2335 'graft',
2335 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2336 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2336 ('', 'base', '',
2337 ('', 'base', '',
2337 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2338 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2338 ('c', 'continue', False, _('resume interrupted graft')),
2339 ('c', 'continue', False, _('resume interrupted graft')),
2339 ('', 'stop', False, _('stop interrupted graft')),
2340 ('', 'stop', False, _('stop interrupted graft')),
2340 ('', 'abort', False, _('abort interrupted graft')),
2341 ('', 'abort', False, _('abort interrupted graft')),
2341 ('e', 'edit', False, _('invoke editor on commit messages')),
2342 ('e', 'edit', False, _('invoke editor on commit messages')),
2342 ('', 'log', None, _('append graft info to log message')),
2343 ('', 'log', None, _('append graft info to log message')),
2343 ('', 'no-commit', None,
2344 ('', 'no-commit', None,
2344 _("don't commit, just apply the changes in working directory")),
2345 _("don't commit, just apply the changes in working directory")),
2345 ('f', 'force', False, _('force graft')),
2346 ('f', 'force', False, _('force graft')),
2346 ('D', 'currentdate', False,
2347 ('D', 'currentdate', False,
2347 _('record the current date as commit date')),
2348 _('record the current date as commit date')),
2348 ('U', 'currentuser', False,
2349 ('U', 'currentuser', False,
2349 _('record the current user as committer'))]
2350 _('record the current user as committer'))]
2350 + commitopts2 + mergetoolopts + dryrunopts,
2351 + commitopts2 + mergetoolopts + dryrunopts,
2351 _('[OPTION]... [-r REV]... REV...'),
2352 _('[OPTION]... [-r REV]... REV...'),
2352 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2353 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2353 def graft(ui, repo, *revs, **opts):
2354 def graft(ui, repo, *revs, **opts):
2354 '''copy changes from other branches onto the current branch
2355 '''copy changes from other branches onto the current branch
2355
2356
2356 This command uses Mercurial's merge logic to copy individual
2357 This command uses Mercurial's merge logic to copy individual
2357 changes from other branches without merging branches in the
2358 changes from other branches without merging branches in the
2358 history graph. This is sometimes known as 'backporting' or
2359 history graph. This is sometimes known as 'backporting' or
2359 'cherry-picking'. By default, graft will copy user, date, and
2360 'cherry-picking'. By default, graft will copy user, date, and
2360 description from the source changesets.
2361 description from the source changesets.
2361
2362
2362 Changesets that are ancestors of the current revision, that have
2363 Changesets that are ancestors of the current revision, that have
2363 already been grafted, or that are merges will be skipped.
2364 already been grafted, or that are merges will be skipped.
2364
2365
2365 If --log is specified, log messages will have a comment appended
2366 If --log is specified, log messages will have a comment appended
2366 of the form::
2367 of the form::
2367
2368
2368 (grafted from CHANGESETHASH)
2369 (grafted from CHANGESETHASH)
2369
2370
2370 If --force is specified, revisions will be grafted even if they
2371 If --force is specified, revisions will be grafted even if they
2371 are already ancestors of, or have been grafted to, the destination.
2372 are already ancestors of, or have been grafted to, the destination.
2372 This is useful when the revisions have since been backed out.
2373 This is useful when the revisions have since been backed out.
2373
2374
2374 If a graft merge results in conflicts, the graft process is
2375 If a graft merge results in conflicts, the graft process is
2375 interrupted so that the current merge can be manually resolved.
2376 interrupted so that the current merge can be manually resolved.
2376 Once all conflicts are addressed, the graft process can be
2377 Once all conflicts are addressed, the graft process can be
2377 continued with the -c/--continue option.
2378 continued with the -c/--continue option.
2378
2379
2379 The -c/--continue option reapplies all the earlier options.
2380 The -c/--continue option reapplies all the earlier options.
2380
2381
2381 .. container:: verbose
2382 .. container:: verbose
2382
2383
2383 The --base option exposes more of how graft internally uses merge with a
2384 The --base option exposes more of how graft internally uses merge with a
2384 custom base revision. --base can be used to specify another ancestor than
2385 custom base revision. --base can be used to specify another ancestor than
2385 the first and only parent.
2386 the first and only parent.
2386
2387
2387 The command::
2388 The command::
2388
2389
2389 hg graft -r 345 --base 234
2390 hg graft -r 345 --base 234
2390
2391
2391 is thus pretty much the same as::
2392 is thus pretty much the same as::
2392
2393
2393 hg diff -r 234 -r 345 | hg import
2394 hg diff -r 234 -r 345 | hg import
2394
2395
2395 but using merge to resolve conflicts and track moved files.
2396 but using merge to resolve conflicts and track moved files.
2396
2397
2397 The result of a merge can thus be backported as a single commit by
2398 The result of a merge can thus be backported as a single commit by
2398 specifying one of the merge parents as base, and thus effectively
2399 specifying one of the merge parents as base, and thus effectively
2399 grafting the changes from the other side.
2400 grafting the changes from the other side.
2400
2401
2401 It is also possible to collapse multiple changesets and clean up history
2402 It is also possible to collapse multiple changesets and clean up history
2402 by specifying another ancestor as base, much like rebase --collapse
2403 by specifying another ancestor as base, much like rebase --collapse
2403 --keep.
2404 --keep.
2404
2405
2405 The commit message can be tweaked after the fact using commit --amend .
2406 The commit message can be tweaked after the fact using commit --amend .
2406
2407
2407 For using non-ancestors as the base to backout changes, see the backout
2408 For using non-ancestors as the base to backout changes, see the backout
2408 command and the hidden --parent option.
2409 command and the hidden --parent option.
2409
2410
2410 .. container:: verbose
2411 .. container:: verbose
2411
2412
2412 Examples:
2413 Examples:
2413
2414
2414 - copy a single change to the stable branch and edit its description::
2415 - copy a single change to the stable branch and edit its description::
2415
2416
2416 hg update stable
2417 hg update stable
2417 hg graft --edit 9393
2418 hg graft --edit 9393
2418
2419
2419 - graft a range of changesets with one exception, updating dates::
2420 - graft a range of changesets with one exception, updating dates::
2420
2421
2421 hg graft -D "2085::2093 and not 2091"
2422 hg graft -D "2085::2093 and not 2091"
2422
2423
2423 - continue a graft after resolving conflicts::
2424 - continue a graft after resolving conflicts::
2424
2425
2425 hg graft -c
2426 hg graft -c
2426
2427
2427 - show the source of a grafted changeset::
2428 - show the source of a grafted changeset::
2428
2429
2429 hg log --debug -r .
2430 hg log --debug -r .
2430
2431
2431 - show revisions sorted by date::
2432 - show revisions sorted by date::
2432
2433
2433 hg log -r "sort(all(), date)"
2434 hg log -r "sort(all(), date)"
2434
2435
2435 - backport the result of a merge as a single commit::
2436 - backport the result of a merge as a single commit::
2436
2437
2437 hg graft -r 123 --base 123^
2438 hg graft -r 123 --base 123^
2438
2439
2439 - land a feature branch as one changeset::
2440 - land a feature branch as one changeset::
2440
2441
2441 hg up -cr default
2442 hg up -cr default
2442 hg graft -r featureX --base "ancestor('featureX', 'default')"
2443 hg graft -r featureX --base "ancestor('featureX', 'default')"
2443
2444
2444 See :hg:`help revisions` for more about specifying revisions.
2445 See :hg:`help revisions` for more about specifying revisions.
2445
2446
2446 Returns 0 on successful completion.
2447 Returns 0 on successful completion.
2447 '''
2448 '''
2448 with repo.wlock():
2449 with repo.wlock():
2449 return _dograft(ui, repo, *revs, **opts)
2450 return _dograft(ui, repo, *revs, **opts)
2450
2451
2451 def _dograft(ui, repo, *revs, **opts):
2452 def _dograft(ui, repo, *revs, **opts):
2452 opts = pycompat.byteskwargs(opts)
2453 opts = pycompat.byteskwargs(opts)
2453 if revs and opts.get('rev'):
2454 if revs and opts.get('rev'):
2454 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2455 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2455 'revision ordering!\n'))
2456 'revision ordering!\n'))
2456
2457
2457 revs = list(revs)
2458 revs = list(revs)
2458 revs.extend(opts.get('rev'))
2459 revs.extend(opts.get('rev'))
2459 basectx = None
2460 basectx = None
2460 if opts.get('base'):
2461 if opts.get('base'):
2461 basectx = scmutil.revsingle(repo, opts['base'], None)
2462 basectx = scmutil.revsingle(repo, opts['base'], None)
2462 # a dict of data to be stored in state file
2463 # a dict of data to be stored in state file
2463 statedata = {}
2464 statedata = {}
2464 # list of new nodes created by ongoing graft
2465 # list of new nodes created by ongoing graft
2465 statedata['newnodes'] = []
2466 statedata['newnodes'] = []
2466
2467
2467 if opts.get('user') and opts.get('currentuser'):
2468 if opts.get('user') and opts.get('currentuser'):
2468 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2469 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2469 if opts.get('date') and opts.get('currentdate'):
2470 if opts.get('date') and opts.get('currentdate'):
2470 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2471 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2471 if not opts.get('user') and opts.get('currentuser'):
2472 if not opts.get('user') and opts.get('currentuser'):
2472 opts['user'] = ui.username()
2473 opts['user'] = ui.username()
2473 if not opts.get('date') and opts.get('currentdate'):
2474 if not opts.get('date') and opts.get('currentdate'):
2474 opts['date'] = "%d %d" % dateutil.makedate()
2475 opts['date'] = "%d %d" % dateutil.makedate()
2475
2476
2476 editor = cmdutil.getcommiteditor(editform='graft',
2477 editor = cmdutil.getcommiteditor(editform='graft',
2477 **pycompat.strkwargs(opts))
2478 **pycompat.strkwargs(opts))
2478
2479
2479 cont = False
2480 cont = False
2480 if opts.get('no_commit'):
2481 if opts.get('no_commit'):
2481 if opts.get('edit'):
2482 if opts.get('edit'):
2482 raise error.Abort(_("cannot specify --no-commit and "
2483 raise error.Abort(_("cannot specify --no-commit and "
2483 "--edit together"))
2484 "--edit together"))
2484 if opts.get('currentuser'):
2485 if opts.get('currentuser'):
2485 raise error.Abort(_("cannot specify --no-commit and "
2486 raise error.Abort(_("cannot specify --no-commit and "
2486 "--currentuser together"))
2487 "--currentuser together"))
2487 if opts.get('currentdate'):
2488 if opts.get('currentdate'):
2488 raise error.Abort(_("cannot specify --no-commit and "
2489 raise error.Abort(_("cannot specify --no-commit and "
2489 "--currentdate together"))
2490 "--currentdate together"))
2490 if opts.get('log'):
2491 if opts.get('log'):
2491 raise error.Abort(_("cannot specify --no-commit and "
2492 raise error.Abort(_("cannot specify --no-commit and "
2492 "--log together"))
2493 "--log together"))
2493
2494
2494 graftstate = statemod.cmdstate(repo, 'graftstate')
2495 graftstate = statemod.cmdstate(repo, 'graftstate')
2495
2496
2496 if opts.get('stop'):
2497 if opts.get('stop'):
2497 if opts.get('continue'):
2498 if opts.get('continue'):
2498 raise error.Abort(_("cannot use '--continue' and "
2499 raise error.Abort(_("cannot use '--continue' and "
2499 "'--stop' together"))
2500 "'--stop' together"))
2500 if opts.get('abort'):
2501 if opts.get('abort'):
2501 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2502 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2502
2503
2503 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2504 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2504 opts.get('date'), opts.get('currentdate'),
2505 opts.get('date'), opts.get('currentdate'),
2505 opts.get('currentuser'), opts.get('rev'))):
2506 opts.get('currentuser'), opts.get('rev'))):
2506 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2507 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2507 return _stopgraft(ui, repo, graftstate)
2508 return _stopgraft(ui, repo, graftstate)
2508 elif opts.get('abort'):
2509 elif opts.get('abort'):
2509 if opts.get('continue'):
2510 if opts.get('continue'):
2510 raise error.Abort(_("cannot use '--continue' and "
2511 raise error.Abort(_("cannot use '--continue' and "
2511 "'--abort' together"))
2512 "'--abort' together"))
2512 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2513 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2513 opts.get('date'), opts.get('currentdate'),
2514 opts.get('date'), opts.get('currentdate'),
2514 opts.get('currentuser'), opts.get('rev'))):
2515 opts.get('currentuser'), opts.get('rev'))):
2515 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2516 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2516
2517
2517 return cmdutil.abortgraft(ui, repo, graftstate)
2518 return cmdutil.abortgraft(ui, repo, graftstate)
2518 elif opts.get('continue'):
2519 elif opts.get('continue'):
2519 cont = True
2520 cont = True
2520 if revs:
2521 if revs:
2521 raise error.Abort(_("can't specify --continue and revisions"))
2522 raise error.Abort(_("can't specify --continue and revisions"))
2522 # read in unfinished revisions
2523 # read in unfinished revisions
2523 if graftstate.exists():
2524 if graftstate.exists():
2524 statedata = cmdutil.readgraftstate(repo, graftstate)
2525 statedata = cmdutil.readgraftstate(repo, graftstate)
2525 if statedata.get('date'):
2526 if statedata.get('date'):
2526 opts['date'] = statedata['date']
2527 opts['date'] = statedata['date']
2527 if statedata.get('user'):
2528 if statedata.get('user'):
2528 opts['user'] = statedata['user']
2529 opts['user'] = statedata['user']
2529 if statedata.get('log'):
2530 if statedata.get('log'):
2530 opts['log'] = True
2531 opts['log'] = True
2531 if statedata.get('no_commit'):
2532 if statedata.get('no_commit'):
2532 opts['no_commit'] = statedata.get('no_commit')
2533 opts['no_commit'] = statedata.get('no_commit')
2533 nodes = statedata['nodes']
2534 nodes = statedata['nodes']
2534 revs = [repo[node].rev() for node in nodes]
2535 revs = [repo[node].rev() for node in nodes]
2535 else:
2536 else:
2536 cmdutil.wrongtooltocontinue(repo, _('graft'))
2537 cmdutil.wrongtooltocontinue(repo, _('graft'))
2537 else:
2538 else:
2538 if not revs:
2539 if not revs:
2539 raise error.Abort(_('no revisions specified'))
2540 raise error.Abort(_('no revisions specified'))
2540 cmdutil.checkunfinished(repo)
2541 cmdutil.checkunfinished(repo)
2541 cmdutil.bailifchanged(repo)
2542 cmdutil.bailifchanged(repo)
2542 revs = scmutil.revrange(repo, revs)
2543 revs = scmutil.revrange(repo, revs)
2543
2544
2544 skipped = set()
2545 skipped = set()
2545 if basectx is None:
2546 if basectx is None:
2546 # check for merges
2547 # check for merges
2547 for rev in repo.revs('%ld and merge()', revs):
2548 for rev in repo.revs('%ld and merge()', revs):
2548 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2549 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2549 skipped.add(rev)
2550 skipped.add(rev)
2550 revs = [r for r in revs if r not in skipped]
2551 revs = [r for r in revs if r not in skipped]
2551 if not revs:
2552 if not revs:
2552 return -1
2553 return -1
2553 if basectx is not None and len(revs) != 1:
2554 if basectx is not None and len(revs) != 1:
2554 raise error.Abort(_('only one revision allowed with --base '))
2555 raise error.Abort(_('only one revision allowed with --base '))
2555
2556
2556 # Don't check in the --continue case, in effect retaining --force across
2557 # Don't check in the --continue case, in effect retaining --force across
2557 # --continues. That's because without --force, any revisions we decided to
2558 # --continues. That's because without --force, any revisions we decided to
2558 # skip would have been filtered out here, so they wouldn't have made their
2559 # skip would have been filtered out here, so they wouldn't have made their
2559 # way to the graftstate. With --force, any revisions we would have otherwise
2560 # way to the graftstate. With --force, any revisions we would have otherwise
2560 # skipped would not have been filtered out, and if they hadn't been applied
2561 # skipped would not have been filtered out, and if they hadn't been applied
2561 # already, they'd have been in the graftstate.
2562 # already, they'd have been in the graftstate.
2562 if not (cont or opts.get('force')) and basectx is None:
2563 if not (cont or opts.get('force')) and basectx is None:
2563 # check for ancestors of dest branch
2564 # check for ancestors of dest branch
2564 crev = repo['.'].rev()
2565 crev = repo['.'].rev()
2565 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2566 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2566 # XXX make this lazy in the future
2567 # XXX make this lazy in the future
2567 # don't mutate while iterating, create a copy
2568 # don't mutate while iterating, create a copy
2568 for rev in list(revs):
2569 for rev in list(revs):
2569 if rev in ancestors:
2570 if rev in ancestors:
2570 ui.warn(_('skipping ancestor revision %d:%s\n') %
2571 ui.warn(_('skipping ancestor revision %d:%s\n') %
2571 (rev, repo[rev]))
2572 (rev, repo[rev]))
2572 # XXX remove on list is slow
2573 # XXX remove on list is slow
2573 revs.remove(rev)
2574 revs.remove(rev)
2574 if not revs:
2575 if not revs:
2575 return -1
2576 return -1
2576
2577
2577 # analyze revs for earlier grafts
2578 # analyze revs for earlier grafts
2578 ids = {}
2579 ids = {}
2579 for ctx in repo.set("%ld", revs):
2580 for ctx in repo.set("%ld", revs):
2580 ids[ctx.hex()] = ctx.rev()
2581 ids[ctx.hex()] = ctx.rev()
2581 n = ctx.extra().get('source')
2582 n = ctx.extra().get('source')
2582 if n:
2583 if n:
2583 ids[n] = ctx.rev()
2584 ids[n] = ctx.rev()
2584
2585
2585 # check ancestors for earlier grafts
2586 # check ancestors for earlier grafts
2586 ui.debug('scanning for duplicate grafts\n')
2587 ui.debug('scanning for duplicate grafts\n')
2587
2588
2588 # The only changesets we can be sure doesn't contain grafts of any
2589 # The only changesets we can be sure doesn't contain grafts of any
2589 # revs, are the ones that are common ancestors of *all* revs:
2590 # revs, are the ones that are common ancestors of *all* revs:
2590 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2591 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2591 ctx = repo[rev]
2592 ctx = repo[rev]
2592 n = ctx.extra().get('source')
2593 n = ctx.extra().get('source')
2593 if n in ids:
2594 if n in ids:
2594 try:
2595 try:
2595 r = repo[n].rev()
2596 r = repo[n].rev()
2596 except error.RepoLookupError:
2597 except error.RepoLookupError:
2597 r = None
2598 r = None
2598 if r in revs:
2599 if r in revs:
2599 ui.warn(_('skipping revision %d:%s '
2600 ui.warn(_('skipping revision %d:%s '
2600 '(already grafted to %d:%s)\n')
2601 '(already grafted to %d:%s)\n')
2601 % (r, repo[r], rev, ctx))
2602 % (r, repo[r], rev, ctx))
2602 revs.remove(r)
2603 revs.remove(r)
2603 elif ids[n] in revs:
2604 elif ids[n] in revs:
2604 if r is None:
2605 if r is None:
2605 ui.warn(_('skipping already grafted revision %d:%s '
2606 ui.warn(_('skipping already grafted revision %d:%s '
2606 '(%d:%s also has unknown origin %s)\n')
2607 '(%d:%s also has unknown origin %s)\n')
2607 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2608 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2608 else:
2609 else:
2609 ui.warn(_('skipping already grafted revision %d:%s '
2610 ui.warn(_('skipping already grafted revision %d:%s '
2610 '(%d:%s also has origin %d:%s)\n')
2611 '(%d:%s also has origin %d:%s)\n')
2611 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2612 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2612 revs.remove(ids[n])
2613 revs.remove(ids[n])
2613 elif ctx.hex() in ids:
2614 elif ctx.hex() in ids:
2614 r = ids[ctx.hex()]
2615 r = ids[ctx.hex()]
2615 if r in revs:
2616 if r in revs:
2616 ui.warn(_('skipping already grafted revision %d:%s '
2617 ui.warn(_('skipping already grafted revision %d:%s '
2617 '(was grafted from %d:%s)\n') %
2618 '(was grafted from %d:%s)\n') %
2618 (r, repo[r], rev, ctx))
2619 (r, repo[r], rev, ctx))
2619 revs.remove(r)
2620 revs.remove(r)
2620 if not revs:
2621 if not revs:
2621 return -1
2622 return -1
2622
2623
2623 if opts.get('no_commit'):
2624 if opts.get('no_commit'):
2624 statedata['no_commit'] = True
2625 statedata['no_commit'] = True
2625 for pos, ctx in enumerate(repo.set("%ld", revs)):
2626 for pos, ctx in enumerate(repo.set("%ld", revs)):
2626 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2627 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2627 ctx.description().split('\n', 1)[0])
2628 ctx.description().split('\n', 1)[0])
2628 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2629 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2629 if names:
2630 if names:
2630 desc += ' (%s)' % ' '.join(names)
2631 desc += ' (%s)' % ' '.join(names)
2631 ui.status(_('grafting %s\n') % desc)
2632 ui.status(_('grafting %s\n') % desc)
2632 if opts.get('dry_run'):
2633 if opts.get('dry_run'):
2633 continue
2634 continue
2634
2635
2635 source = ctx.extra().get('source')
2636 source = ctx.extra().get('source')
2636 extra = {}
2637 extra = {}
2637 if source:
2638 if source:
2638 extra['source'] = source
2639 extra['source'] = source
2639 extra['intermediate-source'] = ctx.hex()
2640 extra['intermediate-source'] = ctx.hex()
2640 else:
2641 else:
2641 extra['source'] = ctx.hex()
2642 extra['source'] = ctx.hex()
2642 user = ctx.user()
2643 user = ctx.user()
2643 if opts.get('user'):
2644 if opts.get('user'):
2644 user = opts['user']
2645 user = opts['user']
2645 statedata['user'] = user
2646 statedata['user'] = user
2646 date = ctx.date()
2647 date = ctx.date()
2647 if opts.get('date'):
2648 if opts.get('date'):
2648 date = opts['date']
2649 date = opts['date']
2649 statedata['date'] = date
2650 statedata['date'] = date
2650 message = ctx.description()
2651 message = ctx.description()
2651 if opts.get('log'):
2652 if opts.get('log'):
2652 message += '\n(grafted from %s)' % ctx.hex()
2653 message += '\n(grafted from %s)' % ctx.hex()
2653 statedata['log'] = True
2654 statedata['log'] = True
2654
2655
2655 # we don't merge the first commit when continuing
2656 # we don't merge the first commit when continuing
2656 if not cont:
2657 if not cont:
2657 # perform the graft merge with p1(rev) as 'ancestor'
2658 # perform the graft merge with p1(rev) as 'ancestor'
2658 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2659 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2659 base = ctx.p1() if basectx is None else basectx
2660 base = ctx.p1() if basectx is None else basectx
2660 with ui.configoverride(overrides, 'graft'):
2661 with ui.configoverride(overrides, 'graft'):
2661 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2662 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2662 # report any conflicts
2663 # report any conflicts
2663 if stats.unresolvedcount > 0:
2664 if stats.unresolvedcount > 0:
2664 # write out state for --continue
2665 # write out state for --continue
2665 nodes = [repo[rev].hex() for rev in revs[pos:]]
2666 nodes = [repo[rev].hex() for rev in revs[pos:]]
2666 statedata['nodes'] = nodes
2667 statedata['nodes'] = nodes
2667 stateversion = 1
2668 stateversion = 1
2668 graftstate.save(stateversion, statedata)
2669 graftstate.save(stateversion, statedata)
2669 hint = _("use 'hg resolve' and 'hg graft --continue'")
2670 hint = _("use 'hg resolve' and 'hg graft --continue'")
2670 raise error.Abort(
2671 raise error.Abort(
2671 _("unresolved conflicts, can't continue"),
2672 _("unresolved conflicts, can't continue"),
2672 hint=hint)
2673 hint=hint)
2673 else:
2674 else:
2674 cont = False
2675 cont = False
2675
2676
2676 # commit if --no-commit is false
2677 # commit if --no-commit is false
2677 if not opts.get('no_commit'):
2678 if not opts.get('no_commit'):
2678 node = repo.commit(text=message, user=user, date=date, extra=extra,
2679 node = repo.commit(text=message, user=user, date=date, extra=extra,
2679 editor=editor)
2680 editor=editor)
2680 if node is None:
2681 if node is None:
2681 ui.warn(
2682 ui.warn(
2682 _('note: graft of %d:%s created no changes to commit\n') %
2683 _('note: graft of %d:%s created no changes to commit\n') %
2683 (ctx.rev(), ctx))
2684 (ctx.rev(), ctx))
2684 # checking that newnodes exist because old state files won't have it
2685 # checking that newnodes exist because old state files won't have it
2685 elif statedata.get('newnodes') is not None:
2686 elif statedata.get('newnodes') is not None:
2686 statedata['newnodes'].append(node)
2687 statedata['newnodes'].append(node)
2687
2688
2688 # remove state when we complete successfully
2689 # remove state when we complete successfully
2689 if not opts.get('dry_run'):
2690 if not opts.get('dry_run'):
2690 graftstate.delete()
2691 graftstate.delete()
2691
2692
2692 return 0
2693 return 0
2693
2694
2694 def _stopgraft(ui, repo, graftstate):
2695 def _stopgraft(ui, repo, graftstate):
2695 """stop the interrupted graft"""
2696 """stop the interrupted graft"""
2696 if not graftstate.exists():
2697 if not graftstate.exists():
2697 raise error.Abort(_("no interrupted graft found"))
2698 raise error.Abort(_("no interrupted graft found"))
2698 pctx = repo['.']
2699 pctx = repo['.']
2699 hg.updaterepo(repo, pctx.node(), overwrite=True)
2700 hg.updaterepo(repo, pctx.node(), overwrite=True)
2700 graftstate.delete()
2701 graftstate.delete()
2701 ui.status(_("stopped the interrupted graft\n"))
2702 ui.status(_("stopped the interrupted graft\n"))
2702 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2703 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2703 return 0
2704 return 0
2704
2705
2705 statemod.addunfinished(
2706 statemod.addunfinished(
2706 'graft', fname='graftstate', clearable=True, stopflag=True,
2707 'graft', fname='graftstate', clearable=True, stopflag=True,
2707 continueflag=True, abortfunc=cmdutil.hgabortgraft,
2708 continueflag=True, abortfunc=cmdutil.hgabortgraft,
2708 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
2709 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
2709 )
2710 )
2710
2711
2711 @command('grep',
2712 @command('grep',
2712 [('0', 'print0', None, _('end fields with NUL')),
2713 [('0', 'print0', None, _('end fields with NUL')),
2713 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2714 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2714 ('', 'diff', None, _('print all revisions when the term was introduced '
2715 ('', 'diff', None, _('print all revisions when the term was introduced '
2715 'or removed')),
2716 'or removed')),
2716 ('a', 'text', None, _('treat all files as text')),
2717 ('a', 'text', None, _('treat all files as text')),
2717 ('f', 'follow', None,
2718 ('f', 'follow', None,
2718 _('follow changeset history,'
2719 _('follow changeset history,'
2719 ' or file history across copies and renames')),
2720 ' or file history across copies and renames')),
2720 ('i', 'ignore-case', None, _('ignore case when matching')),
2721 ('i', 'ignore-case', None, _('ignore case when matching')),
2721 ('l', 'files-with-matches', None,
2722 ('l', 'files-with-matches', None,
2722 _('print only filenames and revisions that match')),
2723 _('print only filenames and revisions that match')),
2723 ('n', 'line-number', None, _('print matching line numbers')),
2724 ('n', 'line-number', None, _('print matching line numbers')),
2724 ('r', 'rev', [],
2725 ('r', 'rev', [],
2725 _('only search files changed within revision range'), _('REV')),
2726 _('only search files changed within revision range'), _('REV')),
2726 ('', 'all-files', None,
2727 ('', 'all-files', None,
2727 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2728 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2728 ('u', 'user', None, _('list the author (long with -v)')),
2729 ('u', 'user', None, _('list the author (long with -v)')),
2729 ('d', 'date', None, _('list the date (short with -q)')),
2730 ('d', 'date', None, _('list the date (short with -q)')),
2730 ] + formatteropts + walkopts,
2731 ] + formatteropts + walkopts,
2731 _('[OPTION]... PATTERN [FILE]...'),
2732 _('[OPTION]... PATTERN [FILE]...'),
2732 helpcategory=command.CATEGORY_FILE_CONTENTS,
2733 helpcategory=command.CATEGORY_FILE_CONTENTS,
2733 inferrepo=True,
2734 inferrepo=True,
2734 intents={INTENT_READONLY})
2735 intents={INTENT_READONLY})
2735 def grep(ui, repo, pattern, *pats, **opts):
2736 def grep(ui, repo, pattern, *pats, **opts):
2736 """search revision history for a pattern in specified files
2737 """search revision history for a pattern in specified files
2737
2738
2738 Search revision history for a regular expression in the specified
2739 Search revision history for a regular expression in the specified
2739 files or the entire project.
2740 files or the entire project.
2740
2741
2741 By default, grep prints the most recent revision number for each
2742 By default, grep prints the most recent revision number for each
2742 file in which it finds a match. To get it to print every revision
2743 file in which it finds a match. To get it to print every revision
2743 that contains a change in match status ("-" for a match that becomes
2744 that contains a change in match status ("-" for a match that becomes
2744 a non-match, or "+" for a non-match that becomes a match), use the
2745 a non-match, or "+" for a non-match that becomes a match), use the
2745 --diff flag.
2746 --diff flag.
2746
2747
2747 PATTERN can be any Python (roughly Perl-compatible) regular
2748 PATTERN can be any Python (roughly Perl-compatible) regular
2748 expression.
2749 expression.
2749
2750
2750 If no FILEs are specified (and -f/--follow isn't set), all files in
2751 If no FILEs are specified (and -f/--follow isn't set), all files in
2751 the repository are searched, including those that don't exist in the
2752 the repository are searched, including those that don't exist in the
2752 current branch or have been deleted in a prior changeset.
2753 current branch or have been deleted in a prior changeset.
2753
2754
2754 .. container:: verbose
2755 .. container:: verbose
2755
2756
2756 Template:
2757 Template:
2757
2758
2758 The following keywords are supported in addition to the common template
2759 The following keywords are supported in addition to the common template
2759 keywords and functions. See also :hg:`help templates`.
2760 keywords and functions. See also :hg:`help templates`.
2760
2761
2761 :change: String. Character denoting insertion ``+`` or removal ``-``.
2762 :change: String. Character denoting insertion ``+`` or removal ``-``.
2762 Available if ``--diff`` is specified.
2763 Available if ``--diff`` is specified.
2763 :lineno: Integer. Line number of the match.
2764 :lineno: Integer. Line number of the match.
2764 :path: String. Repository-absolute path of the file.
2765 :path: String. Repository-absolute path of the file.
2765 :texts: List of text chunks.
2766 :texts: List of text chunks.
2766
2767
2767 And each entry of ``{texts}`` provides the following sub-keywords.
2768 And each entry of ``{texts}`` provides the following sub-keywords.
2768
2769
2769 :matched: Boolean. True if the chunk matches the specified pattern.
2770 :matched: Boolean. True if the chunk matches the specified pattern.
2770 :text: String. Chunk content.
2771 :text: String. Chunk content.
2771
2772
2772 See :hg:`help templates.operators` for the list expansion syntax.
2773 See :hg:`help templates.operators` for the list expansion syntax.
2773
2774
2774 Returns 0 if a match is found, 1 otherwise.
2775 Returns 0 if a match is found, 1 otherwise.
2775 """
2776 """
2776 opts = pycompat.byteskwargs(opts)
2777 opts = pycompat.byteskwargs(opts)
2777 diff = opts.get('all') or opts.get('diff')
2778 diff = opts.get('all') or opts.get('diff')
2778 all_files = opts.get('all_files')
2779 all_files = opts.get('all_files')
2779 if diff and opts.get('all_files'):
2780 if diff and opts.get('all_files'):
2780 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2781 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2781 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2782 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2782 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2783 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2783 # experimental config: commands.grep.all-files
2784 # experimental config: commands.grep.all-files
2784 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2785 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2785 plaingrep = opts.get('all_files') and not opts.get('rev')
2786 plaingrep = opts.get('all_files') and not opts.get('rev')
2786 if plaingrep:
2787 if plaingrep:
2787 opts['rev'] = ['wdir()']
2788 opts['rev'] = ['wdir()']
2788
2789
2789 reflags = re.M
2790 reflags = re.M
2790 if opts.get('ignore_case'):
2791 if opts.get('ignore_case'):
2791 reflags |= re.I
2792 reflags |= re.I
2792 try:
2793 try:
2793 regexp = util.re.compile(pattern, reflags)
2794 regexp = util.re.compile(pattern, reflags)
2794 except re.error as inst:
2795 except re.error as inst:
2795 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2796 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2796 return 1
2797 return 1
2797 sep, eol = ':', '\n'
2798 sep, eol = ':', '\n'
2798 if opts.get('print0'):
2799 if opts.get('print0'):
2799 sep = eol = '\0'
2800 sep = eol = '\0'
2800
2801
2801 getfile = util.lrucachefunc(repo.file)
2802 getfile = util.lrucachefunc(repo.file)
2802
2803
2803 def matchlines(body):
2804 def matchlines(body):
2804 begin = 0
2805 begin = 0
2805 linenum = 0
2806 linenum = 0
2806 while begin < len(body):
2807 while begin < len(body):
2807 match = regexp.search(body, begin)
2808 match = regexp.search(body, begin)
2808 if not match:
2809 if not match:
2809 break
2810 break
2810 mstart, mend = match.span()
2811 mstart, mend = match.span()
2811 linenum += body.count('\n', begin, mstart) + 1
2812 linenum += body.count('\n', begin, mstart) + 1
2812 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2813 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2813 begin = body.find('\n', mend) + 1 or len(body) + 1
2814 begin = body.find('\n', mend) + 1 or len(body) + 1
2814 lend = begin - 1
2815 lend = begin - 1
2815 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2816 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2816
2817
2817 class linestate(object):
2818 class linestate(object):
2818 def __init__(self, line, linenum, colstart, colend):
2819 def __init__(self, line, linenum, colstart, colend):
2819 self.line = line
2820 self.line = line
2820 self.linenum = linenum
2821 self.linenum = linenum
2821 self.colstart = colstart
2822 self.colstart = colstart
2822 self.colend = colend
2823 self.colend = colend
2823
2824
2824 def __hash__(self):
2825 def __hash__(self):
2825 return hash((self.linenum, self.line))
2826 return hash((self.linenum, self.line))
2826
2827
2827 def __eq__(self, other):
2828 def __eq__(self, other):
2828 return self.line == other.line
2829 return self.line == other.line
2829
2830
2830 def findpos(self):
2831 def findpos(self):
2831 """Iterate all (start, end) indices of matches"""
2832 """Iterate all (start, end) indices of matches"""
2832 yield self.colstart, self.colend
2833 yield self.colstart, self.colend
2833 p = self.colend
2834 p = self.colend
2834 while p < len(self.line):
2835 while p < len(self.line):
2835 m = regexp.search(self.line, p)
2836 m = regexp.search(self.line, p)
2836 if not m:
2837 if not m:
2837 break
2838 break
2838 yield m.span()
2839 yield m.span()
2839 p = m.end()
2840 p = m.end()
2840
2841
2841 matches = {}
2842 matches = {}
2842 copies = {}
2843 copies = {}
2843 def grepbody(fn, rev, body):
2844 def grepbody(fn, rev, body):
2844 matches[rev].setdefault(fn, [])
2845 matches[rev].setdefault(fn, [])
2845 m = matches[rev][fn]
2846 m = matches[rev][fn]
2846 for lnum, cstart, cend, line in matchlines(body):
2847 for lnum, cstart, cend, line in matchlines(body):
2847 s = linestate(line, lnum, cstart, cend)
2848 s = linestate(line, lnum, cstart, cend)
2848 m.append(s)
2849 m.append(s)
2849
2850
2850 def difflinestates(a, b):
2851 def difflinestates(a, b):
2851 sm = difflib.SequenceMatcher(None, a, b)
2852 sm = difflib.SequenceMatcher(None, a, b)
2852 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2853 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2853 if tag == r'insert':
2854 if tag == r'insert':
2854 for i in pycompat.xrange(blo, bhi):
2855 for i in pycompat.xrange(blo, bhi):
2855 yield ('+', b[i])
2856 yield ('+', b[i])
2856 elif tag == r'delete':
2857 elif tag == r'delete':
2857 for i in pycompat.xrange(alo, ahi):
2858 for i in pycompat.xrange(alo, ahi):
2858 yield ('-', a[i])
2859 yield ('-', a[i])
2859 elif tag == r'replace':
2860 elif tag == r'replace':
2860 for i in pycompat.xrange(alo, ahi):
2861 for i in pycompat.xrange(alo, ahi):
2861 yield ('-', a[i])
2862 yield ('-', a[i])
2862 for i in pycompat.xrange(blo, bhi):
2863 for i in pycompat.xrange(blo, bhi):
2863 yield ('+', b[i])
2864 yield ('+', b[i])
2864
2865
2865 uipathfn = scmutil.getuipathfn(repo)
2866 uipathfn = scmutil.getuipathfn(repo)
2866 def display(fm, fn, ctx, pstates, states):
2867 def display(fm, fn, ctx, pstates, states):
2867 rev = scmutil.intrev(ctx)
2868 rev = scmutil.intrev(ctx)
2868 if fm.isplain():
2869 if fm.isplain():
2869 formatuser = ui.shortuser
2870 formatuser = ui.shortuser
2870 else:
2871 else:
2871 formatuser = pycompat.bytestr
2872 formatuser = pycompat.bytestr
2872 if ui.quiet:
2873 if ui.quiet:
2873 datefmt = '%Y-%m-%d'
2874 datefmt = '%Y-%m-%d'
2874 else:
2875 else:
2875 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2876 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2876 found = False
2877 found = False
2877 @util.cachefunc
2878 @util.cachefunc
2878 def binary():
2879 def binary():
2879 flog = getfile(fn)
2880 flog = getfile(fn)
2880 try:
2881 try:
2881 return stringutil.binary(flog.read(ctx.filenode(fn)))
2882 return stringutil.binary(flog.read(ctx.filenode(fn)))
2882 except error.WdirUnsupported:
2883 except error.WdirUnsupported:
2883 return ctx[fn].isbinary()
2884 return ctx[fn].isbinary()
2884
2885
2885 fieldnamemap = {'linenumber': 'lineno'}
2886 fieldnamemap = {'linenumber': 'lineno'}
2886 if diff:
2887 if diff:
2887 iter = difflinestates(pstates, states)
2888 iter = difflinestates(pstates, states)
2888 else:
2889 else:
2889 iter = [('', l) for l in states]
2890 iter = [('', l) for l in states]
2890 for change, l in iter:
2891 for change, l in iter:
2891 fm.startitem()
2892 fm.startitem()
2892 fm.context(ctx=ctx)
2893 fm.context(ctx=ctx)
2893 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2894 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2894 fm.plain(uipathfn(fn), label='grep.filename')
2895 fm.plain(uipathfn(fn), label='grep.filename')
2895
2896
2896 cols = [
2897 cols = [
2897 ('rev', '%d', rev, not plaingrep, ''),
2898 ('rev', '%d', rev, not plaingrep, ''),
2898 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2899 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2899 ]
2900 ]
2900 if diff:
2901 if diff:
2901 cols.append(
2902 cols.append(
2902 ('change', '%s', change, True,
2903 ('change', '%s', change, True,
2903 'grep.inserted ' if change == '+' else 'grep.deleted ')
2904 'grep.inserted ' if change == '+' else 'grep.deleted ')
2904 )
2905 )
2905 cols.extend([
2906 cols.extend([
2906 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2907 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2907 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2908 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2908 opts.get('date'), ''),
2909 opts.get('date'), ''),
2909 ])
2910 ])
2910 for name, fmt, data, cond, extra_label in cols:
2911 for name, fmt, data, cond, extra_label in cols:
2911 if cond:
2912 if cond:
2912 fm.plain(sep, label='grep.sep')
2913 fm.plain(sep, label='grep.sep')
2913 field = fieldnamemap.get(name, name)
2914 field = fieldnamemap.get(name, name)
2914 label = extra_label + ('grep.%s' % name)
2915 label = extra_label + ('grep.%s' % name)
2915 fm.condwrite(cond, field, fmt, data, label=label)
2916 fm.condwrite(cond, field, fmt, data, label=label)
2916 if not opts.get('files_with_matches'):
2917 if not opts.get('files_with_matches'):
2917 fm.plain(sep, label='grep.sep')
2918 fm.plain(sep, label='grep.sep')
2918 if not opts.get('text') and binary():
2919 if not opts.get('text') and binary():
2919 fm.plain(_(" Binary file matches"))
2920 fm.plain(_(" Binary file matches"))
2920 else:
2921 else:
2921 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2922 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2922 fm.plain(eol)
2923 fm.plain(eol)
2923 found = True
2924 found = True
2924 if opts.get('files_with_matches'):
2925 if opts.get('files_with_matches'):
2925 break
2926 break
2926 return found
2927 return found
2927
2928
2928 def displaymatches(fm, l):
2929 def displaymatches(fm, l):
2929 p = 0
2930 p = 0
2930 for s, e in l.findpos():
2931 for s, e in l.findpos():
2931 if p < s:
2932 if p < s:
2932 fm.startitem()
2933 fm.startitem()
2933 fm.write('text', '%s', l.line[p:s])
2934 fm.write('text', '%s', l.line[p:s])
2934 fm.data(matched=False)
2935 fm.data(matched=False)
2935 fm.startitem()
2936 fm.startitem()
2936 fm.write('text', '%s', l.line[s:e], label='grep.match')
2937 fm.write('text', '%s', l.line[s:e], label='grep.match')
2937 fm.data(matched=True)
2938 fm.data(matched=True)
2938 p = e
2939 p = e
2939 if p < len(l.line):
2940 if p < len(l.line):
2940 fm.startitem()
2941 fm.startitem()
2941 fm.write('text', '%s', l.line[p:])
2942 fm.write('text', '%s', l.line[p:])
2942 fm.data(matched=False)
2943 fm.data(matched=False)
2943 fm.end()
2944 fm.end()
2944
2945
2945 skip = set()
2946 skip = set()
2946 revfiles = {}
2947 revfiles = {}
2947 match = scmutil.match(repo[None], pats, opts)
2948 match = scmutil.match(repo[None], pats, opts)
2948 found = False
2949 found = False
2949 follow = opts.get('follow')
2950 follow = opts.get('follow')
2950
2951
2951 getrenamed = scmutil.getrenamedfn(repo)
2952 getrenamed = scmutil.getrenamedfn(repo)
2952 def prep(ctx, fns):
2953 def prep(ctx, fns):
2953 rev = ctx.rev()
2954 rev = ctx.rev()
2954 pctx = ctx.p1()
2955 pctx = ctx.p1()
2955 parent = pctx.rev()
2956 parent = pctx.rev()
2956 matches.setdefault(rev, {})
2957 matches.setdefault(rev, {})
2957 matches.setdefault(parent, {})
2958 matches.setdefault(parent, {})
2958 files = revfiles.setdefault(rev, [])
2959 files = revfiles.setdefault(rev, [])
2959 for fn in fns:
2960 for fn in fns:
2960 flog = getfile(fn)
2961 flog = getfile(fn)
2961 try:
2962 try:
2962 fnode = ctx.filenode(fn)
2963 fnode = ctx.filenode(fn)
2963 except error.LookupError:
2964 except error.LookupError:
2964 continue
2965 continue
2965
2966
2966 copy = None
2967 copy = None
2967 if follow:
2968 if follow:
2968 copy = getrenamed(fn, rev)
2969 copy = getrenamed(fn, rev)
2969 if copy:
2970 if copy:
2970 copies.setdefault(rev, {})[fn] = copy
2971 copies.setdefault(rev, {})[fn] = copy
2971 if fn in skip:
2972 if fn in skip:
2972 skip.add(copy)
2973 skip.add(copy)
2973 if fn in skip:
2974 if fn in skip:
2974 continue
2975 continue
2975 files.append(fn)
2976 files.append(fn)
2976
2977
2977 if fn not in matches[rev]:
2978 if fn not in matches[rev]:
2978 try:
2979 try:
2979 content = flog.read(fnode)
2980 content = flog.read(fnode)
2980 except error.WdirUnsupported:
2981 except error.WdirUnsupported:
2981 content = ctx[fn].data()
2982 content = ctx[fn].data()
2982 grepbody(fn, rev, content)
2983 grepbody(fn, rev, content)
2983
2984
2984 pfn = copy or fn
2985 pfn = copy or fn
2985 if pfn not in matches[parent]:
2986 if pfn not in matches[parent]:
2986 try:
2987 try:
2987 fnode = pctx.filenode(pfn)
2988 fnode = pctx.filenode(pfn)
2988 grepbody(pfn, parent, flog.read(fnode))
2989 grepbody(pfn, parent, flog.read(fnode))
2989 except error.LookupError:
2990 except error.LookupError:
2990 pass
2991 pass
2991
2992
2992 ui.pager('grep')
2993 ui.pager('grep')
2993 fm = ui.formatter('grep', opts)
2994 fm = ui.formatter('grep', opts)
2994 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2995 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2995 rev = ctx.rev()
2996 rev = ctx.rev()
2996 parent = ctx.p1().rev()
2997 parent = ctx.p1().rev()
2997 for fn in sorted(revfiles.get(rev, [])):
2998 for fn in sorted(revfiles.get(rev, [])):
2998 states = matches[rev][fn]
2999 states = matches[rev][fn]
2999 copy = copies.get(rev, {}).get(fn)
3000 copy = copies.get(rev, {}).get(fn)
3000 if fn in skip:
3001 if fn in skip:
3001 if copy:
3002 if copy:
3002 skip.add(copy)
3003 skip.add(copy)
3003 continue
3004 continue
3004 pstates = matches.get(parent, {}).get(copy or fn, [])
3005 pstates = matches.get(parent, {}).get(copy or fn, [])
3005 if pstates or states:
3006 if pstates or states:
3006 r = display(fm, fn, ctx, pstates, states)
3007 r = display(fm, fn, ctx, pstates, states)
3007 found = found or r
3008 found = found or r
3008 if r and not diff and not all_files:
3009 if r and not diff and not all_files:
3009 skip.add(fn)
3010 skip.add(fn)
3010 if copy:
3011 if copy:
3011 skip.add(copy)
3012 skip.add(copy)
3012 del revfiles[rev]
3013 del revfiles[rev]
3013 # We will keep the matches dict for the duration of the window
3014 # We will keep the matches dict for the duration of the window
3014 # clear the matches dict once the window is over
3015 # clear the matches dict once the window is over
3015 if not revfiles:
3016 if not revfiles:
3016 matches.clear()
3017 matches.clear()
3017 fm.end()
3018 fm.end()
3018
3019
3019 return not found
3020 return not found
3020
3021
3021 @command('heads',
3022 @command('heads',
3022 [('r', 'rev', '',
3023 [('r', 'rev', '',
3023 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3024 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3024 ('t', 'topo', False, _('show topological heads only')),
3025 ('t', 'topo', False, _('show topological heads only')),
3025 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3026 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3026 ('c', 'closed', False, _('show normal and closed branch heads')),
3027 ('c', 'closed', False, _('show normal and closed branch heads')),
3027 ] + templateopts,
3028 ] + templateopts,
3028 _('[-ct] [-r STARTREV] [REV]...'),
3029 _('[-ct] [-r STARTREV] [REV]...'),
3029 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3030 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3030 intents={INTENT_READONLY})
3031 intents={INTENT_READONLY})
3031 def heads(ui, repo, *branchrevs, **opts):
3032 def heads(ui, repo, *branchrevs, **opts):
3032 """show branch heads
3033 """show branch heads
3033
3034
3034 With no arguments, show all open branch heads in the repository.
3035 With no arguments, show all open branch heads in the repository.
3035 Branch heads are changesets that have no descendants on the
3036 Branch heads are changesets that have no descendants on the
3036 same branch. They are where development generally takes place and
3037 same branch. They are where development generally takes place and
3037 are the usual targets for update and merge operations.
3038 are the usual targets for update and merge operations.
3038
3039
3039 If one or more REVs are given, only open branch heads on the
3040 If one or more REVs are given, only open branch heads on the
3040 branches associated with the specified changesets are shown. This
3041 branches associated with the specified changesets are shown. This
3041 means that you can use :hg:`heads .` to see the heads on the
3042 means that you can use :hg:`heads .` to see the heads on the
3042 currently checked-out branch.
3043 currently checked-out branch.
3043
3044
3044 If -c/--closed is specified, also show branch heads marked closed
3045 If -c/--closed is specified, also show branch heads marked closed
3045 (see :hg:`commit --close-branch`).
3046 (see :hg:`commit --close-branch`).
3046
3047
3047 If STARTREV is specified, only those heads that are descendants of
3048 If STARTREV is specified, only those heads that are descendants of
3048 STARTREV will be displayed.
3049 STARTREV will be displayed.
3049
3050
3050 If -t/--topo is specified, named branch mechanics will be ignored and only
3051 If -t/--topo is specified, named branch mechanics will be ignored and only
3051 topological heads (changesets with no children) will be shown.
3052 topological heads (changesets with no children) will be shown.
3052
3053
3053 Returns 0 if matching heads are found, 1 if not.
3054 Returns 0 if matching heads are found, 1 if not.
3054 """
3055 """
3055
3056
3056 opts = pycompat.byteskwargs(opts)
3057 opts = pycompat.byteskwargs(opts)
3057 start = None
3058 start = None
3058 rev = opts.get('rev')
3059 rev = opts.get('rev')
3059 if rev:
3060 if rev:
3060 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3061 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3061 start = scmutil.revsingle(repo, rev, None).node()
3062 start = scmutil.revsingle(repo, rev, None).node()
3062
3063
3063 if opts.get('topo'):
3064 if opts.get('topo'):
3064 heads = [repo[h] for h in repo.heads(start)]
3065 heads = [repo[h] for h in repo.heads(start)]
3065 else:
3066 else:
3066 heads = []
3067 heads = []
3067 for branch in repo.branchmap():
3068 for branch in repo.branchmap():
3068 heads += repo.branchheads(branch, start, opts.get('closed'))
3069 heads += repo.branchheads(branch, start, opts.get('closed'))
3069 heads = [repo[h] for h in heads]
3070 heads = [repo[h] for h in heads]
3070
3071
3071 if branchrevs:
3072 if branchrevs:
3072 branches = set(repo[r].branch()
3073 branches = set(repo[r].branch()
3073 for r in scmutil.revrange(repo, branchrevs))
3074 for r in scmutil.revrange(repo, branchrevs))
3074 heads = [h for h in heads if h.branch() in branches]
3075 heads = [h for h in heads if h.branch() in branches]
3075
3076
3076 if opts.get('active') and branchrevs:
3077 if opts.get('active') and branchrevs:
3077 dagheads = repo.heads(start)
3078 dagheads = repo.heads(start)
3078 heads = [h for h in heads if h.node() in dagheads]
3079 heads = [h for h in heads if h.node() in dagheads]
3079
3080
3080 if branchrevs:
3081 if branchrevs:
3081 haveheads = set(h.branch() for h in heads)
3082 haveheads = set(h.branch() for h in heads)
3082 if branches - haveheads:
3083 if branches - haveheads:
3083 headless = ', '.join(b for b in branches - haveheads)
3084 headless = ', '.join(b for b in branches - haveheads)
3084 msg = _('no open branch heads found on branches %s')
3085 msg = _('no open branch heads found on branches %s')
3085 if opts.get('rev'):
3086 if opts.get('rev'):
3086 msg += _(' (started at %s)') % opts['rev']
3087 msg += _(' (started at %s)') % opts['rev']
3087 ui.warn((msg + '\n') % headless)
3088 ui.warn((msg + '\n') % headless)
3088
3089
3089 if not heads:
3090 if not heads:
3090 return 1
3091 return 1
3091
3092
3092 ui.pager('heads')
3093 ui.pager('heads')
3093 heads = sorted(heads, key=lambda x: -x.rev())
3094 heads = sorted(heads, key=lambda x: -x.rev())
3094 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3095 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3095 for ctx in heads:
3096 for ctx in heads:
3096 displayer.show(ctx)
3097 displayer.show(ctx)
3097 displayer.close()
3098 displayer.close()
3098
3099
3099 @command('help',
3100 @command('help',
3100 [('e', 'extension', None, _('show only help for extensions')),
3101 [('e', 'extension', None, _('show only help for extensions')),
3101 ('c', 'command', None, _('show only help for commands')),
3102 ('c', 'command', None, _('show only help for commands')),
3102 ('k', 'keyword', None, _('show topics matching keyword')),
3103 ('k', 'keyword', None, _('show topics matching keyword')),
3103 ('s', 'system', [],
3104 ('s', 'system', [],
3104 _('show help for specific platform(s)'), _('PLATFORM')),
3105 _('show help for specific platform(s)'), _('PLATFORM')),
3105 ],
3106 ],
3106 _('[-eck] [-s PLATFORM] [TOPIC]'),
3107 _('[-eck] [-s PLATFORM] [TOPIC]'),
3107 helpcategory=command.CATEGORY_HELP,
3108 helpcategory=command.CATEGORY_HELP,
3108 norepo=True,
3109 norepo=True,
3109 intents={INTENT_READONLY})
3110 intents={INTENT_READONLY})
3110 def help_(ui, name=None, **opts):
3111 def help_(ui, name=None, **opts):
3111 """show help for a given topic or a help overview
3112 """show help for a given topic or a help overview
3112
3113
3113 With no arguments, print a list of commands with short help messages.
3114 With no arguments, print a list of commands with short help messages.
3114
3115
3115 Given a topic, extension, or command name, print help for that
3116 Given a topic, extension, or command name, print help for that
3116 topic.
3117 topic.
3117
3118
3118 Returns 0 if successful.
3119 Returns 0 if successful.
3119 """
3120 """
3120
3121
3121 keep = opts.get(r'system') or []
3122 keep = opts.get(r'system') or []
3122 if len(keep) == 0:
3123 if len(keep) == 0:
3123 if pycompat.sysplatform.startswith('win'):
3124 if pycompat.sysplatform.startswith('win'):
3124 keep.append('windows')
3125 keep.append('windows')
3125 elif pycompat.sysplatform == 'OpenVMS':
3126 elif pycompat.sysplatform == 'OpenVMS':
3126 keep.append('vms')
3127 keep.append('vms')
3127 elif pycompat.sysplatform == 'plan9':
3128 elif pycompat.sysplatform == 'plan9':
3128 keep.append('plan9')
3129 keep.append('plan9')
3129 else:
3130 else:
3130 keep.append('unix')
3131 keep.append('unix')
3131 keep.append(pycompat.sysplatform.lower())
3132 keep.append(pycompat.sysplatform.lower())
3132 if ui.verbose:
3133 if ui.verbose:
3133 keep.append('verbose')
3134 keep.append('verbose')
3134
3135
3135 commands = sys.modules[__name__]
3136 commands = sys.modules[__name__]
3136 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3137 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3137 ui.pager('help')
3138 ui.pager('help')
3138 ui.write(formatted)
3139 ui.write(formatted)
3139
3140
3140
3141
3141 @command('identify|id',
3142 @command('identify|id',
3142 [('r', 'rev', '',
3143 [('r', 'rev', '',
3143 _('identify the specified revision'), _('REV')),
3144 _('identify the specified revision'), _('REV')),
3144 ('n', 'num', None, _('show local revision number')),
3145 ('n', 'num', None, _('show local revision number')),
3145 ('i', 'id', None, _('show global revision id')),
3146 ('i', 'id', None, _('show global revision id')),
3146 ('b', 'branch', None, _('show branch')),
3147 ('b', 'branch', None, _('show branch')),
3147 ('t', 'tags', None, _('show tags')),
3148 ('t', 'tags', None, _('show tags')),
3148 ('B', 'bookmarks', None, _('show bookmarks')),
3149 ('B', 'bookmarks', None, _('show bookmarks')),
3149 ] + remoteopts + formatteropts,
3150 ] + remoteopts + formatteropts,
3150 _('[-nibtB] [-r REV] [SOURCE]'),
3151 _('[-nibtB] [-r REV] [SOURCE]'),
3151 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3152 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3152 optionalrepo=True,
3153 optionalrepo=True,
3153 intents={INTENT_READONLY})
3154 intents={INTENT_READONLY})
3154 def identify(ui, repo, source=None, rev=None,
3155 def identify(ui, repo, source=None, rev=None,
3155 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3156 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3156 """identify the working directory or specified revision
3157 """identify the working directory or specified revision
3157
3158
3158 Print a summary identifying the repository state at REV using one or
3159 Print a summary identifying the repository state at REV using one or
3159 two parent hash identifiers, followed by a "+" if the working
3160 two parent hash identifiers, followed by a "+" if the working
3160 directory has uncommitted changes, the branch name (if not default),
3161 directory has uncommitted changes, the branch name (if not default),
3161 a list of tags, and a list of bookmarks.
3162 a list of tags, and a list of bookmarks.
3162
3163
3163 When REV is not given, print a summary of the current state of the
3164 When REV is not given, print a summary of the current state of the
3164 repository including the working directory. Specify -r. to get information
3165 repository including the working directory. Specify -r. to get information
3165 of the working directory parent without scanning uncommitted changes.
3166 of the working directory parent without scanning uncommitted changes.
3166
3167
3167 Specifying a path to a repository root or Mercurial bundle will
3168 Specifying a path to a repository root or Mercurial bundle will
3168 cause lookup to operate on that repository/bundle.
3169 cause lookup to operate on that repository/bundle.
3169
3170
3170 .. container:: verbose
3171 .. container:: verbose
3171
3172
3172 Template:
3173 Template:
3173
3174
3174 The following keywords are supported in addition to the common template
3175 The following keywords are supported in addition to the common template
3175 keywords and functions. See also :hg:`help templates`.
3176 keywords and functions. See also :hg:`help templates`.
3176
3177
3177 :dirty: String. Character ``+`` denoting if the working directory has
3178 :dirty: String. Character ``+`` denoting if the working directory has
3178 uncommitted changes.
3179 uncommitted changes.
3179 :id: String. One or two nodes, optionally followed by ``+``.
3180 :id: String. One or two nodes, optionally followed by ``+``.
3180 :parents: List of strings. Parent nodes of the changeset.
3181 :parents: List of strings. Parent nodes of the changeset.
3181
3182
3182 Examples:
3183 Examples:
3183
3184
3184 - generate a build identifier for the working directory::
3185 - generate a build identifier for the working directory::
3185
3186
3186 hg id --id > build-id.dat
3187 hg id --id > build-id.dat
3187
3188
3188 - find the revision corresponding to a tag::
3189 - find the revision corresponding to a tag::
3189
3190
3190 hg id -n -r 1.3
3191 hg id -n -r 1.3
3191
3192
3192 - check the most recent revision of a remote repository::
3193 - check the most recent revision of a remote repository::
3193
3194
3194 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3195 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3195
3196
3196 See :hg:`log` for generating more information about specific revisions,
3197 See :hg:`log` for generating more information about specific revisions,
3197 including full hash identifiers.
3198 including full hash identifiers.
3198
3199
3199 Returns 0 if successful.
3200 Returns 0 if successful.
3200 """
3201 """
3201
3202
3202 opts = pycompat.byteskwargs(opts)
3203 opts = pycompat.byteskwargs(opts)
3203 if not repo and not source:
3204 if not repo and not source:
3204 raise error.Abort(_("there is no Mercurial repository here "
3205 raise error.Abort(_("there is no Mercurial repository here "
3205 "(.hg not found)"))
3206 "(.hg not found)"))
3206
3207
3207 default = not (num or id or branch or tags or bookmarks)
3208 default = not (num or id or branch or tags or bookmarks)
3208 output = []
3209 output = []
3209 revs = []
3210 revs = []
3210
3211
3211 if source:
3212 if source:
3212 source, branches = hg.parseurl(ui.expandpath(source))
3213 source, branches = hg.parseurl(ui.expandpath(source))
3213 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3214 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3214 repo = peer.local()
3215 repo = peer.local()
3215 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3216 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3216
3217
3217 fm = ui.formatter('identify', opts)
3218 fm = ui.formatter('identify', opts)
3218 fm.startitem()
3219 fm.startitem()
3219
3220
3220 if not repo:
3221 if not repo:
3221 if num or branch or tags:
3222 if num or branch or tags:
3222 raise error.Abort(
3223 raise error.Abort(
3223 _("can't query remote revision number, branch, or tags"))
3224 _("can't query remote revision number, branch, or tags"))
3224 if not rev and revs:
3225 if not rev and revs:
3225 rev = revs[0]
3226 rev = revs[0]
3226 if not rev:
3227 if not rev:
3227 rev = "tip"
3228 rev = "tip"
3228
3229
3229 remoterev = peer.lookup(rev)
3230 remoterev = peer.lookup(rev)
3230 hexrev = fm.hexfunc(remoterev)
3231 hexrev = fm.hexfunc(remoterev)
3231 if default or id:
3232 if default or id:
3232 output = [hexrev]
3233 output = [hexrev]
3233 fm.data(id=hexrev)
3234 fm.data(id=hexrev)
3234
3235
3235 @util.cachefunc
3236 @util.cachefunc
3236 def getbms():
3237 def getbms():
3237 bms = []
3238 bms = []
3238
3239
3239 if 'bookmarks' in peer.listkeys('namespaces'):
3240 if 'bookmarks' in peer.listkeys('namespaces'):
3240 hexremoterev = hex(remoterev)
3241 hexremoterev = hex(remoterev)
3241 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3242 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3242 if bmr == hexremoterev]
3243 if bmr == hexremoterev]
3243
3244
3244 return sorted(bms)
3245 return sorted(bms)
3245
3246
3246 if fm.isplain():
3247 if fm.isplain():
3247 if bookmarks:
3248 if bookmarks:
3248 output.extend(getbms())
3249 output.extend(getbms())
3249 elif default and not ui.quiet:
3250 elif default and not ui.quiet:
3250 # multiple bookmarks for a single parent separated by '/'
3251 # multiple bookmarks for a single parent separated by '/'
3251 bm = '/'.join(getbms())
3252 bm = '/'.join(getbms())
3252 if bm:
3253 if bm:
3253 output.append(bm)
3254 output.append(bm)
3254 else:
3255 else:
3255 fm.data(node=hex(remoterev))
3256 fm.data(node=hex(remoterev))
3256 if bookmarks or 'bookmarks' in fm.datahint():
3257 if bookmarks or 'bookmarks' in fm.datahint():
3257 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3258 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3258 else:
3259 else:
3259 if rev:
3260 if rev:
3260 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3261 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3261 ctx = scmutil.revsingle(repo, rev, None)
3262 ctx = scmutil.revsingle(repo, rev, None)
3262
3263
3263 if ctx.rev() is None:
3264 if ctx.rev() is None:
3264 ctx = repo[None]
3265 ctx = repo[None]
3265 parents = ctx.parents()
3266 parents = ctx.parents()
3266 taglist = []
3267 taglist = []
3267 for p in parents:
3268 for p in parents:
3268 taglist.extend(p.tags())
3269 taglist.extend(p.tags())
3269
3270
3270 dirty = ""
3271 dirty = ""
3271 if ctx.dirty(missing=True, merge=False, branch=False):
3272 if ctx.dirty(missing=True, merge=False, branch=False):
3272 dirty = '+'
3273 dirty = '+'
3273 fm.data(dirty=dirty)
3274 fm.data(dirty=dirty)
3274
3275
3275 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3276 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3276 if default or id:
3277 if default or id:
3277 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3278 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3278 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3279 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3279
3280
3280 if num:
3281 if num:
3281 numoutput = ["%d" % p.rev() for p in parents]
3282 numoutput = ["%d" % p.rev() for p in parents]
3282 output.append("%s%s" % ('+'.join(numoutput), dirty))
3283 output.append("%s%s" % ('+'.join(numoutput), dirty))
3283
3284
3284 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3285 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3285 for p in parents], name='node'))
3286 for p in parents], name='node'))
3286 else:
3287 else:
3287 hexoutput = fm.hexfunc(ctx.node())
3288 hexoutput = fm.hexfunc(ctx.node())
3288 if default or id:
3289 if default or id:
3289 output = [hexoutput]
3290 output = [hexoutput]
3290 fm.data(id=hexoutput)
3291 fm.data(id=hexoutput)
3291
3292
3292 if num:
3293 if num:
3293 output.append(pycompat.bytestr(ctx.rev()))
3294 output.append(pycompat.bytestr(ctx.rev()))
3294 taglist = ctx.tags()
3295 taglist = ctx.tags()
3295
3296
3296 if default and not ui.quiet:
3297 if default and not ui.quiet:
3297 b = ctx.branch()
3298 b = ctx.branch()
3298 if b != 'default':
3299 if b != 'default':
3299 output.append("(%s)" % b)
3300 output.append("(%s)" % b)
3300
3301
3301 # multiple tags for a single parent separated by '/'
3302 # multiple tags for a single parent separated by '/'
3302 t = '/'.join(taglist)
3303 t = '/'.join(taglist)
3303 if t:
3304 if t:
3304 output.append(t)
3305 output.append(t)
3305
3306
3306 # multiple bookmarks for a single parent separated by '/'
3307 # multiple bookmarks for a single parent separated by '/'
3307 bm = '/'.join(ctx.bookmarks())
3308 bm = '/'.join(ctx.bookmarks())
3308 if bm:
3309 if bm:
3309 output.append(bm)
3310 output.append(bm)
3310 else:
3311 else:
3311 if branch:
3312 if branch:
3312 output.append(ctx.branch())
3313 output.append(ctx.branch())
3313
3314
3314 if tags:
3315 if tags:
3315 output.extend(taglist)
3316 output.extend(taglist)
3316
3317
3317 if bookmarks:
3318 if bookmarks:
3318 output.extend(ctx.bookmarks())
3319 output.extend(ctx.bookmarks())
3319
3320
3320 fm.data(node=ctx.hex())
3321 fm.data(node=ctx.hex())
3321 fm.data(branch=ctx.branch())
3322 fm.data(branch=ctx.branch())
3322 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3323 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3323 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3324 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3324 fm.context(ctx=ctx)
3325 fm.context(ctx=ctx)
3325
3326
3326 fm.plain("%s\n" % ' '.join(output))
3327 fm.plain("%s\n" % ' '.join(output))
3327 fm.end()
3328 fm.end()
3328
3329
3329 @command('import|patch',
3330 @command('import|patch',
3330 [('p', 'strip', 1,
3331 [('p', 'strip', 1,
3331 _('directory strip option for patch. This has the same '
3332 _('directory strip option for patch. This has the same '
3332 'meaning as the corresponding patch option'), _('NUM')),
3333 'meaning as the corresponding patch option'), _('NUM')),
3333 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3334 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3334 ('e', 'edit', False, _('invoke editor on commit messages')),
3335 ('e', 'edit', False, _('invoke editor on commit messages')),
3335 ('f', 'force', None,
3336 ('f', 'force', None,
3336 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3337 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3337 ('', 'no-commit', None,
3338 ('', 'no-commit', None,
3338 _("don't commit, just update the working directory")),
3339 _("don't commit, just update the working directory")),
3339 ('', 'bypass', None,
3340 ('', 'bypass', None,
3340 _("apply patch without touching the working directory")),
3341 _("apply patch without touching the working directory")),
3341 ('', 'partial', None,
3342 ('', 'partial', None,
3342 _('commit even if some hunks fail')),
3343 _('commit even if some hunks fail')),
3343 ('', 'exact', None,
3344 ('', 'exact', None,
3344 _('abort if patch would apply lossily')),
3345 _('abort if patch would apply lossily')),
3345 ('', 'prefix', '',
3346 ('', 'prefix', '',
3346 _('apply patch to subdirectory'), _('DIR')),
3347 _('apply patch to subdirectory'), _('DIR')),
3347 ('', 'import-branch', None,
3348 ('', 'import-branch', None,
3348 _('use any branch information in patch (implied by --exact)'))] +
3349 _('use any branch information in patch (implied by --exact)'))] +
3349 commitopts + commitopts2 + similarityopts,
3350 commitopts + commitopts2 + similarityopts,
3350 _('[OPTION]... PATCH...'),
3351 _('[OPTION]... PATCH...'),
3351 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3352 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3352 def import_(ui, repo, patch1=None, *patches, **opts):
3353 def import_(ui, repo, patch1=None, *patches, **opts):
3353 """import an ordered set of patches
3354 """import an ordered set of patches
3354
3355
3355 Import a list of patches and commit them individually (unless
3356 Import a list of patches and commit them individually (unless
3356 --no-commit is specified).
3357 --no-commit is specified).
3357
3358
3358 To read a patch from standard input (stdin), use "-" as the patch
3359 To read a patch from standard input (stdin), use "-" as the patch
3359 name. If a URL is specified, the patch will be downloaded from
3360 name. If a URL is specified, the patch will be downloaded from
3360 there.
3361 there.
3361
3362
3362 Import first applies changes to the working directory (unless
3363 Import first applies changes to the working directory (unless
3363 --bypass is specified), import will abort if there are outstanding
3364 --bypass is specified), import will abort if there are outstanding
3364 changes.
3365 changes.
3365
3366
3366 Use --bypass to apply and commit patches directly to the
3367 Use --bypass to apply and commit patches directly to the
3367 repository, without affecting the working directory. Without
3368 repository, without affecting the working directory. Without
3368 --exact, patches will be applied on top of the working directory
3369 --exact, patches will be applied on top of the working directory
3369 parent revision.
3370 parent revision.
3370
3371
3371 You can import a patch straight from a mail message. Even patches
3372 You can import a patch straight from a mail message. Even patches
3372 as attachments work (to use the body part, it must have type
3373 as attachments work (to use the body part, it must have type
3373 text/plain or text/x-patch). From and Subject headers of email
3374 text/plain or text/x-patch). From and Subject headers of email
3374 message are used as default committer and commit message. All
3375 message are used as default committer and commit message. All
3375 text/plain body parts before first diff are added to the commit
3376 text/plain body parts before first diff are added to the commit
3376 message.
3377 message.
3377
3378
3378 If the imported patch was generated by :hg:`export`, user and
3379 If the imported patch was generated by :hg:`export`, user and
3379 description from patch override values from message headers and
3380 description from patch override values from message headers and
3380 body. Values given on command line with -m/--message and -u/--user
3381 body. Values given on command line with -m/--message and -u/--user
3381 override these.
3382 override these.
3382
3383
3383 If --exact is specified, import will set the working directory to
3384 If --exact is specified, import will set the working directory to
3384 the parent of each patch before applying it, and will abort if the
3385 the parent of each patch before applying it, and will abort if the
3385 resulting changeset has a different ID than the one recorded in
3386 resulting changeset has a different ID than the one recorded in
3386 the patch. This will guard against various ways that portable
3387 the patch. This will guard against various ways that portable
3387 patch formats and mail systems might fail to transfer Mercurial
3388 patch formats and mail systems might fail to transfer Mercurial
3388 data or metadata. See :hg:`bundle` for lossless transmission.
3389 data or metadata. See :hg:`bundle` for lossless transmission.
3389
3390
3390 Use --partial to ensure a changeset will be created from the patch
3391 Use --partial to ensure a changeset will be created from the patch
3391 even if some hunks fail to apply. Hunks that fail to apply will be
3392 even if some hunks fail to apply. Hunks that fail to apply will be
3392 written to a <target-file>.rej file. Conflicts can then be resolved
3393 written to a <target-file>.rej file. Conflicts can then be resolved
3393 by hand before :hg:`commit --amend` is run to update the created
3394 by hand before :hg:`commit --amend` is run to update the created
3394 changeset. This flag exists to let people import patches that
3395 changeset. This flag exists to let people import patches that
3395 partially apply without losing the associated metadata (author,
3396 partially apply without losing the associated metadata (author,
3396 date, description, ...).
3397 date, description, ...).
3397
3398
3398 .. note::
3399 .. note::
3399
3400
3400 When no hunks apply cleanly, :hg:`import --partial` will create
3401 When no hunks apply cleanly, :hg:`import --partial` will create
3401 an empty changeset, importing only the patch metadata.
3402 an empty changeset, importing only the patch metadata.
3402
3403
3403 With -s/--similarity, hg will attempt to discover renames and
3404 With -s/--similarity, hg will attempt to discover renames and
3404 copies in the patch in the same way as :hg:`addremove`.
3405 copies in the patch in the same way as :hg:`addremove`.
3405
3406
3406 It is possible to use external patch programs to perform the patch
3407 It is possible to use external patch programs to perform the patch
3407 by setting the ``ui.patch`` configuration option. For the default
3408 by setting the ``ui.patch`` configuration option. For the default
3408 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3409 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3409 See :hg:`help config` for more information about configuration
3410 See :hg:`help config` for more information about configuration
3410 files and how to use these options.
3411 files and how to use these options.
3411
3412
3412 See :hg:`help dates` for a list of formats valid for -d/--date.
3413 See :hg:`help dates` for a list of formats valid for -d/--date.
3413
3414
3414 .. container:: verbose
3415 .. container:: verbose
3415
3416
3416 Examples:
3417 Examples:
3417
3418
3418 - import a traditional patch from a website and detect renames::
3419 - import a traditional patch from a website and detect renames::
3419
3420
3420 hg import -s 80 http://example.com/bugfix.patch
3421 hg import -s 80 http://example.com/bugfix.patch
3421
3422
3422 - import a changeset from an hgweb server::
3423 - import a changeset from an hgweb server::
3423
3424
3424 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3425 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3425
3426
3426 - import all the patches in an Unix-style mbox::
3427 - import all the patches in an Unix-style mbox::
3427
3428
3428 hg import incoming-patches.mbox
3429 hg import incoming-patches.mbox
3429
3430
3430 - import patches from stdin::
3431 - import patches from stdin::
3431
3432
3432 hg import -
3433 hg import -
3433
3434
3434 - attempt to exactly restore an exported changeset (not always
3435 - attempt to exactly restore an exported changeset (not always
3435 possible)::
3436 possible)::
3436
3437
3437 hg import --exact proposed-fix.patch
3438 hg import --exact proposed-fix.patch
3438
3439
3439 - use an external tool to apply a patch which is too fuzzy for
3440 - use an external tool to apply a patch which is too fuzzy for
3440 the default internal tool.
3441 the default internal tool.
3441
3442
3442 hg import --config ui.patch="patch --merge" fuzzy.patch
3443 hg import --config ui.patch="patch --merge" fuzzy.patch
3443
3444
3444 - change the default fuzzing from 2 to a less strict 7
3445 - change the default fuzzing from 2 to a less strict 7
3445
3446
3446 hg import --config ui.fuzz=7 fuzz.patch
3447 hg import --config ui.fuzz=7 fuzz.patch
3447
3448
3448 Returns 0 on success, 1 on partial success (see --partial).
3449 Returns 0 on success, 1 on partial success (see --partial).
3449 """
3450 """
3450
3451
3451 opts = pycompat.byteskwargs(opts)
3452 opts = pycompat.byteskwargs(opts)
3452 if not patch1:
3453 if not patch1:
3453 raise error.Abort(_('need at least one patch to import'))
3454 raise error.Abort(_('need at least one patch to import'))
3454
3455
3455 patches = (patch1,) + patches
3456 patches = (patch1,) + patches
3456
3457
3457 date = opts.get('date')
3458 date = opts.get('date')
3458 if date:
3459 if date:
3459 opts['date'] = dateutil.parsedate(date)
3460 opts['date'] = dateutil.parsedate(date)
3460
3461
3461 exact = opts.get('exact')
3462 exact = opts.get('exact')
3462 update = not opts.get('bypass')
3463 update = not opts.get('bypass')
3463 if not update and opts.get('no_commit'):
3464 if not update and opts.get('no_commit'):
3464 raise error.Abort(_('cannot use --no-commit with --bypass'))
3465 raise error.Abort(_('cannot use --no-commit with --bypass'))
3465 try:
3466 try:
3466 sim = float(opts.get('similarity') or 0)
3467 sim = float(opts.get('similarity') or 0)
3467 except ValueError:
3468 except ValueError:
3468 raise error.Abort(_('similarity must be a number'))
3469 raise error.Abort(_('similarity must be a number'))
3469 if sim < 0 or sim > 100:
3470 if sim < 0 or sim > 100:
3470 raise error.Abort(_('similarity must be between 0 and 100'))
3471 raise error.Abort(_('similarity must be between 0 and 100'))
3471 if sim and not update:
3472 if sim and not update:
3472 raise error.Abort(_('cannot use --similarity with --bypass'))
3473 raise error.Abort(_('cannot use --similarity with --bypass'))
3473 if exact:
3474 if exact:
3474 if opts.get('edit'):
3475 if opts.get('edit'):
3475 raise error.Abort(_('cannot use --exact with --edit'))
3476 raise error.Abort(_('cannot use --exact with --edit'))
3476 if opts.get('prefix'):
3477 if opts.get('prefix'):
3477 raise error.Abort(_('cannot use --exact with --prefix'))
3478 raise error.Abort(_('cannot use --exact with --prefix'))
3478
3479
3479 base = opts["base"]
3480 base = opts["base"]
3480 msgs = []
3481 msgs = []
3481 ret = 0
3482 ret = 0
3482
3483
3483 with repo.wlock():
3484 with repo.wlock():
3484 if update:
3485 if update:
3485 cmdutil.checkunfinished(repo)
3486 cmdutil.checkunfinished(repo)
3486 if (exact or not opts.get('force')):
3487 if (exact or not opts.get('force')):
3487 cmdutil.bailifchanged(repo)
3488 cmdutil.bailifchanged(repo)
3488
3489
3489 if not opts.get('no_commit'):
3490 if not opts.get('no_commit'):
3490 lock = repo.lock
3491 lock = repo.lock
3491 tr = lambda: repo.transaction('import')
3492 tr = lambda: repo.transaction('import')
3492 dsguard = util.nullcontextmanager
3493 dsguard = util.nullcontextmanager
3493 else:
3494 else:
3494 lock = util.nullcontextmanager
3495 lock = util.nullcontextmanager
3495 tr = util.nullcontextmanager
3496 tr = util.nullcontextmanager
3496 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3497 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3497 with lock(), tr(), dsguard():
3498 with lock(), tr(), dsguard():
3498 parents = repo[None].parents()
3499 parents = repo[None].parents()
3499 for patchurl in patches:
3500 for patchurl in patches:
3500 if patchurl == '-':
3501 if patchurl == '-':
3501 ui.status(_('applying patch from stdin\n'))
3502 ui.status(_('applying patch from stdin\n'))
3502 patchfile = ui.fin
3503 patchfile = ui.fin
3503 patchurl = 'stdin' # for error message
3504 patchurl = 'stdin' # for error message
3504 else:
3505 else:
3505 patchurl = os.path.join(base, patchurl)
3506 patchurl = os.path.join(base, patchurl)
3506 ui.status(_('applying %s\n') % patchurl)
3507 ui.status(_('applying %s\n') % patchurl)
3507 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3508 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3508
3509
3509 haspatch = False
3510 haspatch = False
3510 for hunk in patch.split(patchfile):
3511 for hunk in patch.split(patchfile):
3511 with patch.extract(ui, hunk) as patchdata:
3512 with patch.extract(ui, hunk) as patchdata:
3512 msg, node, rej = cmdutil.tryimportone(ui, repo,
3513 msg, node, rej = cmdutil.tryimportone(ui, repo,
3513 patchdata,
3514 patchdata,
3514 parents, opts,
3515 parents, opts,
3515 msgs, hg.clean)
3516 msgs, hg.clean)
3516 if msg:
3517 if msg:
3517 haspatch = True
3518 haspatch = True
3518 ui.note(msg + '\n')
3519 ui.note(msg + '\n')
3519 if update or exact:
3520 if update or exact:
3520 parents = repo[None].parents()
3521 parents = repo[None].parents()
3521 else:
3522 else:
3522 parents = [repo[node]]
3523 parents = [repo[node]]
3523 if rej:
3524 if rej:
3524 ui.write_err(_("patch applied partially\n"))
3525 ui.write_err(_("patch applied partially\n"))
3525 ui.write_err(_("(fix the .rej files and run "
3526 ui.write_err(_("(fix the .rej files and run "
3526 "`hg commit --amend`)\n"))
3527 "`hg commit --amend`)\n"))
3527 ret = 1
3528 ret = 1
3528 break
3529 break
3529
3530
3530 if not haspatch:
3531 if not haspatch:
3531 raise error.Abort(_('%s: no diffs found') % patchurl)
3532 raise error.Abort(_('%s: no diffs found') % patchurl)
3532
3533
3533 if msgs:
3534 if msgs:
3534 repo.savecommitmessage('\n* * *\n'.join(msgs))
3535 repo.savecommitmessage('\n* * *\n'.join(msgs))
3535 return ret
3536 return ret
3536
3537
3537 @command('incoming|in',
3538 @command('incoming|in',
3538 [('f', 'force', None,
3539 [('f', 'force', None,
3539 _('run even if remote repository is unrelated')),
3540 _('run even if remote repository is unrelated')),
3540 ('n', 'newest-first', None, _('show newest record first')),
3541 ('n', 'newest-first', None, _('show newest record first')),
3541 ('', 'bundle', '',
3542 ('', 'bundle', '',
3542 _('file to store the bundles into'), _('FILE')),
3543 _('file to store the bundles into'), _('FILE')),
3543 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3544 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3544 ('B', 'bookmarks', False, _("compare bookmarks")),
3545 ('B', 'bookmarks', False, _("compare bookmarks")),
3545 ('b', 'branch', [],
3546 ('b', 'branch', [],
3546 _('a specific branch you would like to pull'), _('BRANCH')),
3547 _('a specific branch you would like to pull'), _('BRANCH')),
3547 ] + logopts + remoteopts + subrepoopts,
3548 ] + logopts + remoteopts + subrepoopts,
3548 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3549 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3549 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3550 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3550 def incoming(ui, repo, source="default", **opts):
3551 def incoming(ui, repo, source="default", **opts):
3551 """show new changesets found in source
3552 """show new changesets found in source
3552
3553
3553 Show new changesets found in the specified path/URL or the default
3554 Show new changesets found in the specified path/URL or the default
3554 pull location. These are the changesets that would have been pulled
3555 pull location. These are the changesets that would have been pulled
3555 by :hg:`pull` at the time you issued this command.
3556 by :hg:`pull` at the time you issued this command.
3556
3557
3557 See pull for valid source format details.
3558 See pull for valid source format details.
3558
3559
3559 .. container:: verbose
3560 .. container:: verbose
3560
3561
3561 With -B/--bookmarks, the result of bookmark comparison between
3562 With -B/--bookmarks, the result of bookmark comparison between
3562 local and remote repositories is displayed. With -v/--verbose,
3563 local and remote repositories is displayed. With -v/--verbose,
3563 status is also displayed for each bookmark like below::
3564 status is also displayed for each bookmark like below::
3564
3565
3565 BM1 01234567890a added
3566 BM1 01234567890a added
3566 BM2 1234567890ab advanced
3567 BM2 1234567890ab advanced
3567 BM3 234567890abc diverged
3568 BM3 234567890abc diverged
3568 BM4 34567890abcd changed
3569 BM4 34567890abcd changed
3569
3570
3570 The action taken locally when pulling depends on the
3571 The action taken locally when pulling depends on the
3571 status of each bookmark:
3572 status of each bookmark:
3572
3573
3573 :``added``: pull will create it
3574 :``added``: pull will create it
3574 :``advanced``: pull will update it
3575 :``advanced``: pull will update it
3575 :``diverged``: pull will create a divergent bookmark
3576 :``diverged``: pull will create a divergent bookmark
3576 :``changed``: result depends on remote changesets
3577 :``changed``: result depends on remote changesets
3577
3578
3578 From the point of view of pulling behavior, bookmark
3579 From the point of view of pulling behavior, bookmark
3579 existing only in the remote repository are treated as ``added``,
3580 existing only in the remote repository are treated as ``added``,
3580 even if it is in fact locally deleted.
3581 even if it is in fact locally deleted.
3581
3582
3582 .. container:: verbose
3583 .. container:: verbose
3583
3584
3584 For remote repository, using --bundle avoids downloading the
3585 For remote repository, using --bundle avoids downloading the
3585 changesets twice if the incoming is followed by a pull.
3586 changesets twice if the incoming is followed by a pull.
3586
3587
3587 Examples:
3588 Examples:
3588
3589
3589 - show incoming changes with patches and full description::
3590 - show incoming changes with patches and full description::
3590
3591
3591 hg incoming -vp
3592 hg incoming -vp
3592
3593
3593 - show incoming changes excluding merges, store a bundle::
3594 - show incoming changes excluding merges, store a bundle::
3594
3595
3595 hg in -vpM --bundle incoming.hg
3596 hg in -vpM --bundle incoming.hg
3596 hg pull incoming.hg
3597 hg pull incoming.hg
3597
3598
3598 - briefly list changes inside a bundle::
3599 - briefly list changes inside a bundle::
3599
3600
3600 hg in changes.hg -T "{desc|firstline}\\n"
3601 hg in changes.hg -T "{desc|firstline}\\n"
3601
3602
3602 Returns 0 if there are incoming changes, 1 otherwise.
3603 Returns 0 if there are incoming changes, 1 otherwise.
3603 """
3604 """
3604 opts = pycompat.byteskwargs(opts)
3605 opts = pycompat.byteskwargs(opts)
3605 if opts.get('graph'):
3606 if opts.get('graph'):
3606 logcmdutil.checkunsupportedgraphflags([], opts)
3607 logcmdutil.checkunsupportedgraphflags([], opts)
3607 def display(other, chlist, displayer):
3608 def display(other, chlist, displayer):
3608 revdag = logcmdutil.graphrevs(other, chlist, opts)
3609 revdag = logcmdutil.graphrevs(other, chlist, opts)
3609 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3610 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3610 graphmod.asciiedges)
3611 graphmod.asciiedges)
3611
3612
3612 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3613 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3613 return 0
3614 return 0
3614
3615
3615 if opts.get('bundle') and opts.get('subrepos'):
3616 if opts.get('bundle') and opts.get('subrepos'):
3616 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3617 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3617
3618
3618 if opts.get('bookmarks'):
3619 if opts.get('bookmarks'):
3619 source, branches = hg.parseurl(ui.expandpath(source),
3620 source, branches = hg.parseurl(ui.expandpath(source),
3620 opts.get('branch'))
3621 opts.get('branch'))
3621 other = hg.peer(repo, opts, source)
3622 other = hg.peer(repo, opts, source)
3622 if 'bookmarks' not in other.listkeys('namespaces'):
3623 if 'bookmarks' not in other.listkeys('namespaces'):
3623 ui.warn(_("remote doesn't support bookmarks\n"))
3624 ui.warn(_("remote doesn't support bookmarks\n"))
3624 return 0
3625 return 0
3625 ui.pager('incoming')
3626 ui.pager('incoming')
3626 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3627 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3627 return bookmarks.incoming(ui, repo, other)
3628 return bookmarks.incoming(ui, repo, other)
3628
3629
3629 repo._subtoppath = ui.expandpath(source)
3630 repo._subtoppath = ui.expandpath(source)
3630 try:
3631 try:
3631 return hg.incoming(ui, repo, source, opts)
3632 return hg.incoming(ui, repo, source, opts)
3632 finally:
3633 finally:
3633 del repo._subtoppath
3634 del repo._subtoppath
3634
3635
3635
3636
3636 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3637 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3637 helpcategory=command.CATEGORY_REPO_CREATION,
3638 helpcategory=command.CATEGORY_REPO_CREATION,
3638 helpbasic=True, norepo=True)
3639 helpbasic=True, norepo=True)
3639 def init(ui, dest=".", **opts):
3640 def init(ui, dest=".", **opts):
3640 """create a new repository in the given directory
3641 """create a new repository in the given directory
3641
3642
3642 Initialize a new repository in the given directory. If the given
3643 Initialize a new repository in the given directory. If the given
3643 directory does not exist, it will be created.
3644 directory does not exist, it will be created.
3644
3645
3645 If no directory is given, the current directory is used.
3646 If no directory is given, the current directory is used.
3646
3647
3647 It is possible to specify an ``ssh://`` URL as the destination.
3648 It is possible to specify an ``ssh://`` URL as the destination.
3648 See :hg:`help urls` for more information.
3649 See :hg:`help urls` for more information.
3649
3650
3650 Returns 0 on success.
3651 Returns 0 on success.
3651 """
3652 """
3652 opts = pycompat.byteskwargs(opts)
3653 opts = pycompat.byteskwargs(opts)
3653 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3654 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3654
3655
3655 @command('locate',
3656 @command('locate',
3656 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3657 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3657 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3658 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3658 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3659 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3659 ] + walkopts,
3660 ] + walkopts,
3660 _('[OPTION]... [PATTERN]...'),
3661 _('[OPTION]... [PATTERN]...'),
3661 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3662 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3662 def locate(ui, repo, *pats, **opts):
3663 def locate(ui, repo, *pats, **opts):
3663 """locate files matching specific patterns (DEPRECATED)
3664 """locate files matching specific patterns (DEPRECATED)
3664
3665
3665 Print files under Mercurial control in the working directory whose
3666 Print files under Mercurial control in the working directory whose
3666 names match the given patterns.
3667 names match the given patterns.
3667
3668
3668 By default, this command searches all directories in the working
3669 By default, this command searches all directories in the working
3669 directory. To search just the current directory and its
3670 directory. To search just the current directory and its
3670 subdirectories, use "--include .".
3671 subdirectories, use "--include .".
3671
3672
3672 If no patterns are given to match, this command prints the names
3673 If no patterns are given to match, this command prints the names
3673 of all files under Mercurial control in the working directory.
3674 of all files under Mercurial control in the working directory.
3674
3675
3675 If you want to feed the output of this command into the "xargs"
3676 If you want to feed the output of this command into the "xargs"
3676 command, use the -0 option to both this command and "xargs". This
3677 command, use the -0 option to both this command and "xargs". This
3677 will avoid the problem of "xargs" treating single filenames that
3678 will avoid the problem of "xargs" treating single filenames that
3678 contain whitespace as multiple filenames.
3679 contain whitespace as multiple filenames.
3679
3680
3680 See :hg:`help files` for a more versatile command.
3681 See :hg:`help files` for a more versatile command.
3681
3682
3682 Returns 0 if a match is found, 1 otherwise.
3683 Returns 0 if a match is found, 1 otherwise.
3683 """
3684 """
3684 opts = pycompat.byteskwargs(opts)
3685 opts = pycompat.byteskwargs(opts)
3685 if opts.get('print0'):
3686 if opts.get('print0'):
3686 end = '\0'
3687 end = '\0'
3687 else:
3688 else:
3688 end = '\n'
3689 end = '\n'
3689 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3690 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3690
3691
3691 ret = 1
3692 ret = 1
3692 m = scmutil.match(ctx, pats, opts, default='relglob',
3693 m = scmutil.match(ctx, pats, opts, default='relglob',
3693 badfn=lambda x, y: False)
3694 badfn=lambda x, y: False)
3694
3695
3695 ui.pager('locate')
3696 ui.pager('locate')
3696 if ctx.rev() is None:
3697 if ctx.rev() is None:
3697 # When run on the working copy, "locate" includes removed files, so
3698 # When run on the working copy, "locate" includes removed files, so
3698 # we get the list of files from the dirstate.
3699 # we get the list of files from the dirstate.
3699 filesgen = sorted(repo.dirstate.matches(m))
3700 filesgen = sorted(repo.dirstate.matches(m))
3700 else:
3701 else:
3701 filesgen = ctx.matches(m)
3702 filesgen = ctx.matches(m)
3702 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3703 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3703 for abs in filesgen:
3704 for abs in filesgen:
3704 if opts.get('fullpath'):
3705 if opts.get('fullpath'):
3705 ui.write(repo.wjoin(abs), end)
3706 ui.write(repo.wjoin(abs), end)
3706 else:
3707 else:
3707 ui.write(uipathfn(abs), end)
3708 ui.write(uipathfn(abs), end)
3708 ret = 0
3709 ret = 0
3709
3710
3710 return ret
3711 return ret
3711
3712
3712 @command('log|history',
3713 @command('log|history',
3713 [('f', 'follow', None,
3714 [('f', 'follow', None,
3714 _('follow changeset history, or file history across copies and renames')),
3715 _('follow changeset history, or file history across copies and renames')),
3715 ('', 'follow-first', None,
3716 ('', 'follow-first', None,
3716 _('only follow the first parent of merge changesets (DEPRECATED)')),
3717 _('only follow the first parent of merge changesets (DEPRECATED)')),
3717 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3718 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3718 ('C', 'copies', None, _('show copied files')),
3719 ('C', 'copies', None, _('show copied files')),
3719 ('k', 'keyword', [],
3720 ('k', 'keyword', [],
3720 _('do case-insensitive search for a given text'), _('TEXT')),
3721 _('do case-insensitive search for a given text'), _('TEXT')),
3721 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3722 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3722 ('L', 'line-range', [],
3723 ('L', 'line-range', [],
3723 _('follow line range of specified file (EXPERIMENTAL)'),
3724 _('follow line range of specified file (EXPERIMENTAL)'),
3724 _('FILE,RANGE')),
3725 _('FILE,RANGE')),
3725 ('', 'removed', None, _('include revisions where files were removed')),
3726 ('', 'removed', None, _('include revisions where files were removed')),
3726 ('m', 'only-merges', None,
3727 ('m', 'only-merges', None,
3727 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3728 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3728 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3729 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3729 ('', 'only-branch', [],
3730 ('', 'only-branch', [],
3730 _('show only changesets within the given named branch (DEPRECATED)'),
3731 _('show only changesets within the given named branch (DEPRECATED)'),
3731 _('BRANCH')),
3732 _('BRANCH')),
3732 ('b', 'branch', [],
3733 ('b', 'branch', [],
3733 _('show changesets within the given named branch'), _('BRANCH')),
3734 _('show changesets within the given named branch'), _('BRANCH')),
3734 ('P', 'prune', [],
3735 ('P', 'prune', [],
3735 _('do not display revision or any of its ancestors'), _('REV')),
3736 _('do not display revision or any of its ancestors'), _('REV')),
3736 ] + logopts + walkopts,
3737 ] + logopts + walkopts,
3737 _('[OPTION]... [FILE]'),
3738 _('[OPTION]... [FILE]'),
3738 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3739 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3739 helpbasic=True, inferrepo=True,
3740 helpbasic=True, inferrepo=True,
3740 intents={INTENT_READONLY})
3741 intents={INTENT_READONLY})
3741 def log(ui, repo, *pats, **opts):
3742 def log(ui, repo, *pats, **opts):
3742 """show revision history of entire repository or files
3743 """show revision history of entire repository or files
3743
3744
3744 Print the revision history of the specified files or the entire
3745 Print the revision history of the specified files or the entire
3745 project.
3746 project.
3746
3747
3747 If no revision range is specified, the default is ``tip:0`` unless
3748 If no revision range is specified, the default is ``tip:0`` unless
3748 --follow is set, in which case the working directory parent is
3749 --follow is set, in which case the working directory parent is
3749 used as the starting revision.
3750 used as the starting revision.
3750
3751
3751 File history is shown without following rename or copy history of
3752 File history is shown without following rename or copy history of
3752 files. Use -f/--follow with a filename to follow history across
3753 files. Use -f/--follow with a filename to follow history across
3753 renames and copies. --follow without a filename will only show
3754 renames and copies. --follow without a filename will only show
3754 ancestors of the starting revision.
3755 ancestors of the starting revision.
3755
3756
3756 By default this command prints revision number and changeset id,
3757 By default this command prints revision number and changeset id,
3757 tags, non-trivial parents, user, date and time, and a summary for
3758 tags, non-trivial parents, user, date and time, and a summary for
3758 each commit. When the -v/--verbose switch is used, the list of
3759 each commit. When the -v/--verbose switch is used, the list of
3759 changed files and full commit message are shown.
3760 changed files and full commit message are shown.
3760
3761
3761 With --graph the revisions are shown as an ASCII art DAG with the most
3762 With --graph the revisions are shown as an ASCII art DAG with the most
3762 recent changeset at the top.
3763 recent changeset at the top.
3763 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3764 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3764 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3765 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3765 changeset from the lines below is a parent of the 'o' merge on the same
3766 changeset from the lines below is a parent of the 'o' merge on the same
3766 line.
3767 line.
3767 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3768 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3768 of a '|' indicates one or more revisions in a path are omitted.
3769 of a '|' indicates one or more revisions in a path are omitted.
3769
3770
3770 .. container:: verbose
3771 .. container:: verbose
3771
3772
3772 Use -L/--line-range FILE,M:N options to follow the history of lines
3773 Use -L/--line-range FILE,M:N options to follow the history of lines
3773 from M to N in FILE. With -p/--patch only diff hunks affecting
3774 from M to N in FILE. With -p/--patch only diff hunks affecting
3774 specified line range will be shown. This option requires --follow;
3775 specified line range will be shown. This option requires --follow;
3775 it can be specified multiple times. Currently, this option is not
3776 it can be specified multiple times. Currently, this option is not
3776 compatible with --graph. This option is experimental.
3777 compatible with --graph. This option is experimental.
3777
3778
3778 .. note::
3779 .. note::
3779
3780
3780 :hg:`log --patch` may generate unexpected diff output for merge
3781 :hg:`log --patch` may generate unexpected diff output for merge
3781 changesets, as it will only compare the merge changeset against
3782 changesets, as it will only compare the merge changeset against
3782 its first parent. Also, only files different from BOTH parents
3783 its first parent. Also, only files different from BOTH parents
3783 will appear in files:.
3784 will appear in files:.
3784
3785
3785 .. note::
3786 .. note::
3786
3787
3787 For performance reasons, :hg:`log FILE` may omit duplicate changes
3788 For performance reasons, :hg:`log FILE` may omit duplicate changes
3788 made on branches and will not show removals or mode changes. To
3789 made on branches and will not show removals or mode changes. To
3789 see all such changes, use the --removed switch.
3790 see all such changes, use the --removed switch.
3790
3791
3791 .. container:: verbose
3792 .. container:: verbose
3792
3793
3793 .. note::
3794 .. note::
3794
3795
3795 The history resulting from -L/--line-range options depends on diff
3796 The history resulting from -L/--line-range options depends on diff
3796 options; for instance if white-spaces are ignored, respective changes
3797 options; for instance if white-spaces are ignored, respective changes
3797 with only white-spaces in specified line range will not be listed.
3798 with only white-spaces in specified line range will not be listed.
3798
3799
3799 .. container:: verbose
3800 .. container:: verbose
3800
3801
3801 Some examples:
3802 Some examples:
3802
3803
3803 - changesets with full descriptions and file lists::
3804 - changesets with full descriptions and file lists::
3804
3805
3805 hg log -v
3806 hg log -v
3806
3807
3807 - changesets ancestral to the working directory::
3808 - changesets ancestral to the working directory::
3808
3809
3809 hg log -f
3810 hg log -f
3810
3811
3811 - last 10 commits on the current branch::
3812 - last 10 commits on the current branch::
3812
3813
3813 hg log -l 10 -b .
3814 hg log -l 10 -b .
3814
3815
3815 - changesets showing all modifications of a file, including removals::
3816 - changesets showing all modifications of a file, including removals::
3816
3817
3817 hg log --removed file.c
3818 hg log --removed file.c
3818
3819
3819 - all changesets that touch a directory, with diffs, excluding merges::
3820 - all changesets that touch a directory, with diffs, excluding merges::
3820
3821
3821 hg log -Mp lib/
3822 hg log -Mp lib/
3822
3823
3823 - all revision numbers that match a keyword::
3824 - all revision numbers that match a keyword::
3824
3825
3825 hg log -k bug --template "{rev}\\n"
3826 hg log -k bug --template "{rev}\\n"
3826
3827
3827 - the full hash identifier of the working directory parent::
3828 - the full hash identifier of the working directory parent::
3828
3829
3829 hg log -r . --template "{node}\\n"
3830 hg log -r . --template "{node}\\n"
3830
3831
3831 - list available log templates::
3832 - list available log templates::
3832
3833
3833 hg log -T list
3834 hg log -T list
3834
3835
3835 - check if a given changeset is included in a tagged release::
3836 - check if a given changeset is included in a tagged release::
3836
3837
3837 hg log -r "a21ccf and ancestor(1.9)"
3838 hg log -r "a21ccf and ancestor(1.9)"
3838
3839
3839 - find all changesets by some user in a date range::
3840 - find all changesets by some user in a date range::
3840
3841
3841 hg log -k alice -d "may 2008 to jul 2008"
3842 hg log -k alice -d "may 2008 to jul 2008"
3842
3843
3843 - summary of all changesets after the last tag::
3844 - summary of all changesets after the last tag::
3844
3845
3845 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3846 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3846
3847
3847 - changesets touching lines 13 to 23 for file.c::
3848 - changesets touching lines 13 to 23 for file.c::
3848
3849
3849 hg log -L file.c,13:23
3850 hg log -L file.c,13:23
3850
3851
3851 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3852 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3852 main.c with patch::
3853 main.c with patch::
3853
3854
3854 hg log -L file.c,13:23 -L main.c,2:6 -p
3855 hg log -L file.c,13:23 -L main.c,2:6 -p
3855
3856
3856 See :hg:`help dates` for a list of formats valid for -d/--date.
3857 See :hg:`help dates` for a list of formats valid for -d/--date.
3857
3858
3858 See :hg:`help revisions` for more about specifying and ordering
3859 See :hg:`help revisions` for more about specifying and ordering
3859 revisions.
3860 revisions.
3860
3861
3861 See :hg:`help templates` for more about pre-packaged styles and
3862 See :hg:`help templates` for more about pre-packaged styles and
3862 specifying custom templates. The default template used by the log
3863 specifying custom templates. The default template used by the log
3863 command can be customized via the ``ui.logtemplate`` configuration
3864 command can be customized via the ``ui.logtemplate`` configuration
3864 setting.
3865 setting.
3865
3866
3866 Returns 0 on success.
3867 Returns 0 on success.
3867
3868
3868 """
3869 """
3869 opts = pycompat.byteskwargs(opts)
3870 opts = pycompat.byteskwargs(opts)
3870 linerange = opts.get('line_range')
3871 linerange = opts.get('line_range')
3871
3872
3872 if linerange and not opts.get('follow'):
3873 if linerange and not opts.get('follow'):
3873 raise error.Abort(_('--line-range requires --follow'))
3874 raise error.Abort(_('--line-range requires --follow'))
3874
3875
3875 if linerange and pats:
3876 if linerange and pats:
3876 # TODO: take pats as patterns with no line-range filter
3877 # TODO: take pats as patterns with no line-range filter
3877 raise error.Abort(
3878 raise error.Abort(
3878 _('FILE arguments are not compatible with --line-range option')
3879 _('FILE arguments are not compatible with --line-range option')
3879 )
3880 )
3880
3881
3881 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3882 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3882 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3883 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3883 if linerange:
3884 if linerange:
3884 # TODO: should follow file history from logcmdutil._initialrevs(),
3885 # TODO: should follow file history from logcmdutil._initialrevs(),
3885 # then filter the result by logcmdutil._makerevset() and --limit
3886 # then filter the result by logcmdutil._makerevset() and --limit
3886 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3887 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3887
3888
3888 getcopies = None
3889 getcopies = None
3889 if opts.get('copies'):
3890 if opts.get('copies'):
3890 endrev = None
3891 endrev = None
3891 if revs:
3892 if revs:
3892 endrev = revs.max() + 1
3893 endrev = revs.max() + 1
3893 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
3894 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
3894
3895
3895 ui.pager('log')
3896 ui.pager('log')
3896 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3897 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3897 buffered=True)
3898 buffered=True)
3898 if opts.get('graph'):
3899 if opts.get('graph'):
3899 displayfn = logcmdutil.displaygraphrevs
3900 displayfn = logcmdutil.displaygraphrevs
3900 else:
3901 else:
3901 displayfn = logcmdutil.displayrevs
3902 displayfn = logcmdutil.displayrevs
3902 displayfn(ui, repo, revs, displayer, getcopies)
3903 displayfn(ui, repo, revs, displayer, getcopies)
3903
3904
3904 @command('manifest',
3905 @command('manifest',
3905 [('r', 'rev', '', _('revision to display'), _('REV')),
3906 [('r', 'rev', '', _('revision to display'), _('REV')),
3906 ('', 'all', False, _("list files from all revisions"))]
3907 ('', 'all', False, _("list files from all revisions"))]
3907 + formatteropts,
3908 + formatteropts,
3908 _('[-r REV]'),
3909 _('[-r REV]'),
3909 helpcategory=command.CATEGORY_MAINTENANCE,
3910 helpcategory=command.CATEGORY_MAINTENANCE,
3910 intents={INTENT_READONLY})
3911 intents={INTENT_READONLY})
3911 def manifest(ui, repo, node=None, rev=None, **opts):
3912 def manifest(ui, repo, node=None, rev=None, **opts):
3912 """output the current or given revision of the project manifest
3913 """output the current or given revision of the project manifest
3913
3914
3914 Print a list of version controlled files for the given revision.
3915 Print a list of version controlled files for the given revision.
3915 If no revision is given, the first parent of the working directory
3916 If no revision is given, the first parent of the working directory
3916 is used, or the null revision if no revision is checked out.
3917 is used, or the null revision if no revision is checked out.
3917
3918
3918 With -v, print file permissions, symlink and executable bits.
3919 With -v, print file permissions, symlink and executable bits.
3919 With --debug, print file revision hashes.
3920 With --debug, print file revision hashes.
3920
3921
3921 If option --all is specified, the list of all files from all revisions
3922 If option --all is specified, the list of all files from all revisions
3922 is printed. This includes deleted and renamed files.
3923 is printed. This includes deleted and renamed files.
3923
3924
3924 Returns 0 on success.
3925 Returns 0 on success.
3925 """
3926 """
3926 opts = pycompat.byteskwargs(opts)
3927 opts = pycompat.byteskwargs(opts)
3927 fm = ui.formatter('manifest', opts)
3928 fm = ui.formatter('manifest', opts)
3928
3929
3929 if opts.get('all'):
3930 if opts.get('all'):
3930 if rev or node:
3931 if rev or node:
3931 raise error.Abort(_("can't specify a revision with --all"))
3932 raise error.Abort(_("can't specify a revision with --all"))
3932
3933
3933 res = set()
3934 res = set()
3934 for rev in repo:
3935 for rev in repo:
3935 ctx = repo[rev]
3936 ctx = repo[rev]
3936 res |= set(ctx.files())
3937 res |= set(ctx.files())
3937
3938
3938 ui.pager('manifest')
3939 ui.pager('manifest')
3939 for f in sorted(res):
3940 for f in sorted(res):
3940 fm.startitem()
3941 fm.startitem()
3941 fm.write("path", '%s\n', f)
3942 fm.write("path", '%s\n', f)
3942 fm.end()
3943 fm.end()
3943 return
3944 return
3944
3945
3945 if rev and node:
3946 if rev and node:
3946 raise error.Abort(_("please specify just one revision"))
3947 raise error.Abort(_("please specify just one revision"))
3947
3948
3948 if not node:
3949 if not node:
3949 node = rev
3950 node = rev
3950
3951
3951 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3952 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3952 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3953 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3953 if node:
3954 if node:
3954 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3955 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3955 ctx = scmutil.revsingle(repo, node)
3956 ctx = scmutil.revsingle(repo, node)
3956 mf = ctx.manifest()
3957 mf = ctx.manifest()
3957 ui.pager('manifest')
3958 ui.pager('manifest')
3958 for f in ctx:
3959 for f in ctx:
3959 fm.startitem()
3960 fm.startitem()
3960 fm.context(ctx=ctx)
3961 fm.context(ctx=ctx)
3961 fl = ctx[f].flags()
3962 fl = ctx[f].flags()
3962 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3963 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3963 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3964 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3964 fm.write('path', '%s\n', f)
3965 fm.write('path', '%s\n', f)
3965 fm.end()
3966 fm.end()
3966
3967
3967 @command('merge',
3968 @command('merge',
3968 [('f', 'force', None,
3969 [('f', 'force', None,
3969 _('force a merge including outstanding changes (DEPRECATED)')),
3970 _('force a merge including outstanding changes (DEPRECATED)')),
3970 ('r', 'rev', '', _('revision to merge'), _('REV')),
3971 ('r', 'rev', '', _('revision to merge'), _('REV')),
3971 ('P', 'preview', None,
3972 ('P', 'preview', None,
3972 _('review revisions to merge (no merge is performed)')),
3973 _('review revisions to merge (no merge is performed)')),
3973 ('', 'abort', None, _('abort the ongoing merge')),
3974 ('', 'abort', None, _('abort the ongoing merge')),
3974 ] + mergetoolopts,
3975 ] + mergetoolopts,
3975 _('[-P] [[-r] REV]'),
3976 _('[-P] [[-r] REV]'),
3976 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3977 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3977 def merge(ui, repo, node=None, **opts):
3978 def merge(ui, repo, node=None, **opts):
3978 """merge another revision into working directory
3979 """merge another revision into working directory
3979
3980
3980 The current working directory is updated with all changes made in
3981 The current working directory is updated with all changes made in
3981 the requested revision since the last common predecessor revision.
3982 the requested revision since the last common predecessor revision.
3982
3983
3983 Files that changed between either parent are marked as changed for
3984 Files that changed between either parent are marked as changed for
3984 the next commit and a commit must be performed before any further
3985 the next commit and a commit must be performed before any further
3985 updates to the repository are allowed. The next commit will have
3986 updates to the repository are allowed. The next commit will have
3986 two parents.
3987 two parents.
3987
3988
3988 ``--tool`` can be used to specify the merge tool used for file
3989 ``--tool`` can be used to specify the merge tool used for file
3989 merges. It overrides the HGMERGE environment variable and your
3990 merges. It overrides the HGMERGE environment variable and your
3990 configuration files. See :hg:`help merge-tools` for options.
3991 configuration files. See :hg:`help merge-tools` for options.
3991
3992
3992 If no revision is specified, the working directory's parent is a
3993 If no revision is specified, the working directory's parent is a
3993 head revision, and the current branch contains exactly one other
3994 head revision, and the current branch contains exactly one other
3994 head, the other head is merged with by default. Otherwise, an
3995 head, the other head is merged with by default. Otherwise, an
3995 explicit revision with which to merge must be provided.
3996 explicit revision with which to merge must be provided.
3996
3997
3997 See :hg:`help resolve` for information on handling file conflicts.
3998 See :hg:`help resolve` for information on handling file conflicts.
3998
3999
3999 To undo an uncommitted merge, use :hg:`merge --abort` which
4000 To undo an uncommitted merge, use :hg:`merge --abort` which
4000 will check out a clean copy of the original merge parent, losing
4001 will check out a clean copy of the original merge parent, losing
4001 all changes.
4002 all changes.
4002
4003
4003 Returns 0 on success, 1 if there are unresolved files.
4004 Returns 0 on success, 1 if there are unresolved files.
4004 """
4005 """
4005
4006
4006 opts = pycompat.byteskwargs(opts)
4007 opts = pycompat.byteskwargs(opts)
4007 abort = opts.get('abort')
4008 abort = opts.get('abort')
4008 if abort and repo.dirstate.p2() == nullid:
4009 if abort and repo.dirstate.p2() == nullid:
4009 cmdutil.wrongtooltocontinue(repo, _('merge'))
4010 cmdutil.wrongtooltocontinue(repo, _('merge'))
4010 if abort:
4011 if abort:
4011 state = cmdutil.getunfinishedstate(repo)
4012 state = cmdutil.getunfinishedstate(repo)
4012 if state and state._opname != 'merge':
4013 if state and state._opname != 'merge':
4013 raise error.Abort(_('cannot abort merge with %s in progress') %
4014 raise error.Abort(_('cannot abort merge with %s in progress') %
4014 (state._opname), hint=state.hint())
4015 (state._opname), hint=state.hint())
4015 if node:
4016 if node:
4016 raise error.Abort(_("cannot specify a node with --abort"))
4017 raise error.Abort(_("cannot specify a node with --abort"))
4017 if opts.get('rev'):
4018 if opts.get('rev'):
4018 raise error.Abort(_("cannot specify both --rev and --abort"))
4019 raise error.Abort(_("cannot specify both --rev and --abort"))
4019 if opts.get('preview'):
4020 if opts.get('preview'):
4020 raise error.Abort(_("cannot specify --preview with --abort"))
4021 raise error.Abort(_("cannot specify --preview with --abort"))
4021 if opts.get('rev') and node:
4022 if opts.get('rev') and node:
4022 raise error.Abort(_("please specify just one revision"))
4023 raise error.Abort(_("please specify just one revision"))
4023 if not node:
4024 if not node:
4024 node = opts.get('rev')
4025 node = opts.get('rev')
4025
4026
4026 if node:
4027 if node:
4027 node = scmutil.revsingle(repo, node).node()
4028 node = scmutil.revsingle(repo, node).node()
4028
4029
4029 if not node and not abort:
4030 if not node and not abort:
4030 node = repo[destutil.destmerge(repo)].node()
4031 node = repo[destutil.destmerge(repo)].node()
4031
4032
4032 if opts.get('preview'):
4033 if opts.get('preview'):
4033 # find nodes that are ancestors of p2 but not of p1
4034 # find nodes that are ancestors of p2 but not of p1
4034 p1 = repo.lookup('.')
4035 p1 = repo.lookup('.')
4035 p2 = node
4036 p2 = node
4036 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4037 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4037
4038
4038 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4039 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4039 for node in nodes:
4040 for node in nodes:
4040 displayer.show(repo[node])
4041 displayer.show(repo[node])
4041 displayer.close()
4042 displayer.close()
4042 return 0
4043 return 0
4043
4044
4044 # ui.forcemerge is an internal variable, do not document
4045 # ui.forcemerge is an internal variable, do not document
4045 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4046 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4046 with ui.configoverride(overrides, 'merge'):
4047 with ui.configoverride(overrides, 'merge'):
4047 force = opts.get('force')
4048 force = opts.get('force')
4048 labels = ['working copy', 'merge rev']
4049 labels = ['working copy', 'merge rev']
4049 return hg.merge(repo, node, force=force, mergeforce=force,
4050 return hg.merge(repo, node, force=force, mergeforce=force,
4050 labels=labels, abort=abort)
4051 labels=labels, abort=abort)
4051
4052
4052 statemod.addunfinished(
4053 statemod.addunfinished(
4053 'merge', fname=None, clearable=True, allowcommit=True,
4054 'merge', fname=None, clearable=True, allowcommit=True,
4054 cmdmsg=_('outstanding uncommitted merge'), abortfunc=hg.abortmerge,
4055 cmdmsg=_('outstanding uncommitted merge'), abortfunc=hg.abortmerge,
4055 statushint=_('To continue: hg commit\n'
4056 statushint=_('To continue: hg commit\n'
4056 'To abort: hg merge --abort'),
4057 'To abort: hg merge --abort'),
4057 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
4058 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
4058 )
4059 )
4059
4060
4060 @command('outgoing|out',
4061 @command('outgoing|out',
4061 [('f', 'force', None, _('run even when the destination is unrelated')),
4062 [('f', 'force', None, _('run even when the destination is unrelated')),
4062 ('r', 'rev', [],
4063 ('r', 'rev', [],
4063 _('a changeset intended to be included in the destination'), _('REV')),
4064 _('a changeset intended to be included in the destination'), _('REV')),
4064 ('n', 'newest-first', None, _('show newest record first')),
4065 ('n', 'newest-first', None, _('show newest record first')),
4065 ('B', 'bookmarks', False, _('compare bookmarks')),
4066 ('B', 'bookmarks', False, _('compare bookmarks')),
4066 ('b', 'branch', [], _('a specific branch you would like to push'),
4067 ('b', 'branch', [], _('a specific branch you would like to push'),
4067 _('BRANCH')),
4068 _('BRANCH')),
4068 ] + logopts + remoteopts + subrepoopts,
4069 ] + logopts + remoteopts + subrepoopts,
4069 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4070 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4070 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4071 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4071 def outgoing(ui, repo, dest=None, **opts):
4072 def outgoing(ui, repo, dest=None, **opts):
4072 """show changesets not found in the destination
4073 """show changesets not found in the destination
4073
4074
4074 Show changesets not found in the specified destination repository
4075 Show changesets not found in the specified destination repository
4075 or the default push location. These are the changesets that would
4076 or the default push location. These are the changesets that would
4076 be pushed if a push was requested.
4077 be pushed if a push was requested.
4077
4078
4078 See pull for details of valid destination formats.
4079 See pull for details of valid destination formats.
4079
4080
4080 .. container:: verbose
4081 .. container:: verbose
4081
4082
4082 With -B/--bookmarks, the result of bookmark comparison between
4083 With -B/--bookmarks, the result of bookmark comparison between
4083 local and remote repositories is displayed. With -v/--verbose,
4084 local and remote repositories is displayed. With -v/--verbose,
4084 status is also displayed for each bookmark like below::
4085 status is also displayed for each bookmark like below::
4085
4086
4086 BM1 01234567890a added
4087 BM1 01234567890a added
4087 BM2 deleted
4088 BM2 deleted
4088 BM3 234567890abc advanced
4089 BM3 234567890abc advanced
4089 BM4 34567890abcd diverged
4090 BM4 34567890abcd diverged
4090 BM5 4567890abcde changed
4091 BM5 4567890abcde changed
4091
4092
4092 The action taken when pushing depends on the
4093 The action taken when pushing depends on the
4093 status of each bookmark:
4094 status of each bookmark:
4094
4095
4095 :``added``: push with ``-B`` will create it
4096 :``added``: push with ``-B`` will create it
4096 :``deleted``: push with ``-B`` will delete it
4097 :``deleted``: push with ``-B`` will delete it
4097 :``advanced``: push will update it
4098 :``advanced``: push will update it
4098 :``diverged``: push with ``-B`` will update it
4099 :``diverged``: push with ``-B`` will update it
4099 :``changed``: push with ``-B`` will update it
4100 :``changed``: push with ``-B`` will update it
4100
4101
4101 From the point of view of pushing behavior, bookmarks
4102 From the point of view of pushing behavior, bookmarks
4102 existing only in the remote repository are treated as
4103 existing only in the remote repository are treated as
4103 ``deleted``, even if it is in fact added remotely.
4104 ``deleted``, even if it is in fact added remotely.
4104
4105
4105 Returns 0 if there are outgoing changes, 1 otherwise.
4106 Returns 0 if there are outgoing changes, 1 otherwise.
4106 """
4107 """
4107 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4108 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4108 # style URLs, so don't overwrite dest.
4109 # style URLs, so don't overwrite dest.
4109 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4110 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4110 if not path:
4111 if not path:
4111 raise error.Abort(_('default repository not configured!'),
4112 raise error.Abort(_('default repository not configured!'),
4112 hint=_("see 'hg help config.paths'"))
4113 hint=_("see 'hg help config.paths'"))
4113
4114
4114 opts = pycompat.byteskwargs(opts)
4115 opts = pycompat.byteskwargs(opts)
4115 if opts.get('graph'):
4116 if opts.get('graph'):
4116 logcmdutil.checkunsupportedgraphflags([], opts)
4117 logcmdutil.checkunsupportedgraphflags([], opts)
4117 o, other = hg._outgoing(ui, repo, dest, opts)
4118 o, other = hg._outgoing(ui, repo, dest, opts)
4118 if not o:
4119 if not o:
4119 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4120 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4120 return
4121 return
4121
4122
4122 revdag = logcmdutil.graphrevs(repo, o, opts)
4123 revdag = logcmdutil.graphrevs(repo, o, opts)
4123 ui.pager('outgoing')
4124 ui.pager('outgoing')
4124 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4125 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4125 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4126 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4126 graphmod.asciiedges)
4127 graphmod.asciiedges)
4127 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4128 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4128 return 0
4129 return 0
4129
4130
4130 if opts.get('bookmarks'):
4131 if opts.get('bookmarks'):
4131 dest = path.pushloc or path.loc
4132 dest = path.pushloc or path.loc
4132 other = hg.peer(repo, opts, dest)
4133 other = hg.peer(repo, opts, dest)
4133 if 'bookmarks' not in other.listkeys('namespaces'):
4134 if 'bookmarks' not in other.listkeys('namespaces'):
4134 ui.warn(_("remote doesn't support bookmarks\n"))
4135 ui.warn(_("remote doesn't support bookmarks\n"))
4135 return 0
4136 return 0
4136 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4137 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4137 ui.pager('outgoing')
4138 ui.pager('outgoing')
4138 return bookmarks.outgoing(ui, repo, other)
4139 return bookmarks.outgoing(ui, repo, other)
4139
4140
4140 repo._subtoppath = path.pushloc or path.loc
4141 repo._subtoppath = path.pushloc or path.loc
4141 try:
4142 try:
4142 return hg.outgoing(ui, repo, dest, opts)
4143 return hg.outgoing(ui, repo, dest, opts)
4143 finally:
4144 finally:
4144 del repo._subtoppath
4145 del repo._subtoppath
4145
4146
4146 @command('parents',
4147 @command('parents',
4147 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4148 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4148 ] + templateopts,
4149 ] + templateopts,
4149 _('[-r REV] [FILE]'),
4150 _('[-r REV] [FILE]'),
4150 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4151 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4151 inferrepo=True)
4152 inferrepo=True)
4152 def parents(ui, repo, file_=None, **opts):
4153 def parents(ui, repo, file_=None, **opts):
4153 """show the parents of the working directory or revision (DEPRECATED)
4154 """show the parents of the working directory or revision (DEPRECATED)
4154
4155
4155 Print the working directory's parent revisions. If a revision is
4156 Print the working directory's parent revisions. If a revision is
4156 given via -r/--rev, the parent of that revision will be printed.
4157 given via -r/--rev, the parent of that revision will be printed.
4157 If a file argument is given, the revision in which the file was
4158 If a file argument is given, the revision in which the file was
4158 last changed (before the working directory revision or the
4159 last changed (before the working directory revision or the
4159 argument to --rev if given) is printed.
4160 argument to --rev if given) is printed.
4160
4161
4161 This command is equivalent to::
4162 This command is equivalent to::
4162
4163
4163 hg log -r "p1()+p2()" or
4164 hg log -r "p1()+p2()" or
4164 hg log -r "p1(REV)+p2(REV)" or
4165 hg log -r "p1(REV)+p2(REV)" or
4165 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4166 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4166 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4167 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4167
4168
4168 See :hg:`summary` and :hg:`help revsets` for related information.
4169 See :hg:`summary` and :hg:`help revsets` for related information.
4169
4170
4170 Returns 0 on success.
4171 Returns 0 on success.
4171 """
4172 """
4172
4173
4173 opts = pycompat.byteskwargs(opts)
4174 opts = pycompat.byteskwargs(opts)
4174 rev = opts.get('rev')
4175 rev = opts.get('rev')
4175 if rev:
4176 if rev:
4176 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4177 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4177 ctx = scmutil.revsingle(repo, rev, None)
4178 ctx = scmutil.revsingle(repo, rev, None)
4178
4179
4179 if file_:
4180 if file_:
4180 m = scmutil.match(ctx, (file_,), opts)
4181 m = scmutil.match(ctx, (file_,), opts)
4181 if m.anypats() or len(m.files()) != 1:
4182 if m.anypats() or len(m.files()) != 1:
4182 raise error.Abort(_('can only specify an explicit filename'))
4183 raise error.Abort(_('can only specify an explicit filename'))
4183 file_ = m.files()[0]
4184 file_ = m.files()[0]
4184 filenodes = []
4185 filenodes = []
4185 for cp in ctx.parents():
4186 for cp in ctx.parents():
4186 if not cp:
4187 if not cp:
4187 continue
4188 continue
4188 try:
4189 try:
4189 filenodes.append(cp.filenode(file_))
4190 filenodes.append(cp.filenode(file_))
4190 except error.LookupError:
4191 except error.LookupError:
4191 pass
4192 pass
4192 if not filenodes:
4193 if not filenodes:
4193 raise error.Abort(_("'%s' not found in manifest!") % file_)
4194 raise error.Abort(_("'%s' not found in manifest!") % file_)
4194 p = []
4195 p = []
4195 for fn in filenodes:
4196 for fn in filenodes:
4196 fctx = repo.filectx(file_, fileid=fn)
4197 fctx = repo.filectx(file_, fileid=fn)
4197 p.append(fctx.node())
4198 p.append(fctx.node())
4198 else:
4199 else:
4199 p = [cp.node() for cp in ctx.parents()]
4200 p = [cp.node() for cp in ctx.parents()]
4200
4201
4201 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4202 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4202 for n in p:
4203 for n in p:
4203 if n != nullid:
4204 if n != nullid:
4204 displayer.show(repo[n])
4205 displayer.show(repo[n])
4205 displayer.close()
4206 displayer.close()
4206
4207
4207 @command('paths', formatteropts, _('[NAME]'),
4208 @command('paths', formatteropts, _('[NAME]'),
4208 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4209 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4209 optionalrepo=True, intents={INTENT_READONLY})
4210 optionalrepo=True, intents={INTENT_READONLY})
4210 def paths(ui, repo, search=None, **opts):
4211 def paths(ui, repo, search=None, **opts):
4211 """show aliases for remote repositories
4212 """show aliases for remote repositories
4212
4213
4213 Show definition of symbolic path name NAME. If no name is given,
4214 Show definition of symbolic path name NAME. If no name is given,
4214 show definition of all available names.
4215 show definition of all available names.
4215
4216
4216 Option -q/--quiet suppresses all output when searching for NAME
4217 Option -q/--quiet suppresses all output when searching for NAME
4217 and shows only the path names when listing all definitions.
4218 and shows only the path names when listing all definitions.
4218
4219
4219 Path names are defined in the [paths] section of your
4220 Path names are defined in the [paths] section of your
4220 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4221 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4221 repository, ``.hg/hgrc`` is used, too.
4222 repository, ``.hg/hgrc`` is used, too.
4222
4223
4223 The path names ``default`` and ``default-push`` have a special
4224 The path names ``default`` and ``default-push`` have a special
4224 meaning. When performing a push or pull operation, they are used
4225 meaning. When performing a push or pull operation, they are used
4225 as fallbacks if no location is specified on the command-line.
4226 as fallbacks if no location is specified on the command-line.
4226 When ``default-push`` is set, it will be used for push and
4227 When ``default-push`` is set, it will be used for push and
4227 ``default`` will be used for pull; otherwise ``default`` is used
4228 ``default`` will be used for pull; otherwise ``default`` is used
4228 as the fallback for both. When cloning a repository, the clone
4229 as the fallback for both. When cloning a repository, the clone
4229 source is written as ``default`` in ``.hg/hgrc``.
4230 source is written as ``default`` in ``.hg/hgrc``.
4230
4231
4231 .. note::
4232 .. note::
4232
4233
4233 ``default`` and ``default-push`` apply to all inbound (e.g.
4234 ``default`` and ``default-push`` apply to all inbound (e.g.
4234 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4235 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4235 and :hg:`bundle`) operations.
4236 and :hg:`bundle`) operations.
4236
4237
4237 See :hg:`help urls` for more information.
4238 See :hg:`help urls` for more information.
4238
4239
4239 .. container:: verbose
4240 .. container:: verbose
4240
4241
4241 Template:
4242 Template:
4242
4243
4243 The following keywords are supported. See also :hg:`help templates`.
4244 The following keywords are supported. See also :hg:`help templates`.
4244
4245
4245 :name: String. Symbolic name of the path alias.
4246 :name: String. Symbolic name of the path alias.
4246 :pushurl: String. URL for push operations.
4247 :pushurl: String. URL for push operations.
4247 :url: String. URL or directory path for the other operations.
4248 :url: String. URL or directory path for the other operations.
4248
4249
4249 Returns 0 on success.
4250 Returns 0 on success.
4250 """
4251 """
4251
4252
4252 opts = pycompat.byteskwargs(opts)
4253 opts = pycompat.byteskwargs(opts)
4253 ui.pager('paths')
4254 ui.pager('paths')
4254 if search:
4255 if search:
4255 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4256 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4256 if name == search]
4257 if name == search]
4257 else:
4258 else:
4258 pathitems = sorted(ui.paths.iteritems())
4259 pathitems = sorted(ui.paths.iteritems())
4259
4260
4260 fm = ui.formatter('paths', opts)
4261 fm = ui.formatter('paths', opts)
4261 if fm.isplain():
4262 if fm.isplain():
4262 hidepassword = util.hidepassword
4263 hidepassword = util.hidepassword
4263 else:
4264 else:
4264 hidepassword = bytes
4265 hidepassword = bytes
4265 if ui.quiet:
4266 if ui.quiet:
4266 namefmt = '%s\n'
4267 namefmt = '%s\n'
4267 else:
4268 else:
4268 namefmt = '%s = '
4269 namefmt = '%s = '
4269 showsubopts = not search and not ui.quiet
4270 showsubopts = not search and not ui.quiet
4270
4271
4271 for name, path in pathitems:
4272 for name, path in pathitems:
4272 fm.startitem()
4273 fm.startitem()
4273 fm.condwrite(not search, 'name', namefmt, name)
4274 fm.condwrite(not search, 'name', namefmt, name)
4274 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4275 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4275 for subopt, value in sorted(path.suboptions.items()):
4276 for subopt, value in sorted(path.suboptions.items()):
4276 assert subopt not in ('name', 'url')
4277 assert subopt not in ('name', 'url')
4277 if showsubopts:
4278 if showsubopts:
4278 fm.plain('%s:%s = ' % (name, subopt))
4279 fm.plain('%s:%s = ' % (name, subopt))
4279 fm.condwrite(showsubopts, subopt, '%s\n', value)
4280 fm.condwrite(showsubopts, subopt, '%s\n', value)
4280
4281
4281 fm.end()
4282 fm.end()
4282
4283
4283 if search and not pathitems:
4284 if search and not pathitems:
4284 if not ui.quiet:
4285 if not ui.quiet:
4285 ui.warn(_("not found!\n"))
4286 ui.warn(_("not found!\n"))
4286 return 1
4287 return 1
4287 else:
4288 else:
4288 return 0
4289 return 0
4289
4290
4290 @command('phase',
4291 @command('phase',
4291 [('p', 'public', False, _('set changeset phase to public')),
4292 [('p', 'public', False, _('set changeset phase to public')),
4292 ('d', 'draft', False, _('set changeset phase to draft')),
4293 ('d', 'draft', False, _('set changeset phase to draft')),
4293 ('s', 'secret', False, _('set changeset phase to secret')),
4294 ('s', 'secret', False, _('set changeset phase to secret')),
4294 ('f', 'force', False, _('allow to move boundary backward')),
4295 ('f', 'force', False, _('allow to move boundary backward')),
4295 ('r', 'rev', [], _('target revision'), _('REV')),
4296 ('r', 'rev', [], _('target revision'), _('REV')),
4296 ],
4297 ],
4297 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4298 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4298 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4299 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4299 def phase(ui, repo, *revs, **opts):
4300 def phase(ui, repo, *revs, **opts):
4300 """set or show the current phase name
4301 """set or show the current phase name
4301
4302
4302 With no argument, show the phase name of the current revision(s).
4303 With no argument, show the phase name of the current revision(s).
4303
4304
4304 With one of -p/--public, -d/--draft or -s/--secret, change the
4305 With one of -p/--public, -d/--draft or -s/--secret, change the
4305 phase value of the specified revisions.
4306 phase value of the specified revisions.
4306
4307
4307 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4308 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4308 lower phase to a higher phase. Phases are ordered as follows::
4309 lower phase to a higher phase. Phases are ordered as follows::
4309
4310
4310 public < draft < secret
4311 public < draft < secret
4311
4312
4312 Returns 0 on success, 1 if some phases could not be changed.
4313 Returns 0 on success, 1 if some phases could not be changed.
4313
4314
4314 (For more information about the phases concept, see :hg:`help phases`.)
4315 (For more information about the phases concept, see :hg:`help phases`.)
4315 """
4316 """
4316 opts = pycompat.byteskwargs(opts)
4317 opts = pycompat.byteskwargs(opts)
4317 # search for a unique phase argument
4318 # search for a unique phase argument
4318 targetphase = None
4319 targetphase = None
4319 for idx, name in enumerate(phases.cmdphasenames):
4320 for idx, name in enumerate(phases.cmdphasenames):
4320 if opts[name]:
4321 if opts[name]:
4321 if targetphase is not None:
4322 if targetphase is not None:
4322 raise error.Abort(_('only one phase can be specified'))
4323 raise error.Abort(_('only one phase can be specified'))
4323 targetphase = idx
4324 targetphase = idx
4324
4325
4325 # look for specified revision
4326 # look for specified revision
4326 revs = list(revs)
4327 revs = list(revs)
4327 revs.extend(opts['rev'])
4328 revs.extend(opts['rev'])
4328 if not revs:
4329 if not revs:
4329 # display both parents as the second parent phase can influence
4330 # display both parents as the second parent phase can influence
4330 # the phase of a merge commit
4331 # the phase of a merge commit
4331 revs = [c.rev() for c in repo[None].parents()]
4332 revs = [c.rev() for c in repo[None].parents()]
4332
4333
4333 revs = scmutil.revrange(repo, revs)
4334 revs = scmutil.revrange(repo, revs)
4334
4335
4335 ret = 0
4336 ret = 0
4336 if targetphase is None:
4337 if targetphase is None:
4337 # display
4338 # display
4338 for r in revs:
4339 for r in revs:
4339 ctx = repo[r]
4340 ctx = repo[r]
4340 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4341 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4341 else:
4342 else:
4342 with repo.lock(), repo.transaction("phase") as tr:
4343 with repo.lock(), repo.transaction("phase") as tr:
4343 # set phase
4344 # set phase
4344 if not revs:
4345 if not revs:
4345 raise error.Abort(_('empty revision set'))
4346 raise error.Abort(_('empty revision set'))
4346 nodes = [repo[r].node() for r in revs]
4347 nodes = [repo[r].node() for r in revs]
4347 # moving revision from public to draft may hide them
4348 # moving revision from public to draft may hide them
4348 # We have to check result on an unfiltered repository
4349 # We have to check result on an unfiltered repository
4349 unfi = repo.unfiltered()
4350 unfi = repo.unfiltered()
4350 getphase = unfi._phasecache.phase
4351 getphase = unfi._phasecache.phase
4351 olddata = [getphase(unfi, r) for r in unfi]
4352 olddata = [getphase(unfi, r) for r in unfi]
4352 phases.advanceboundary(repo, tr, targetphase, nodes)
4353 phases.advanceboundary(repo, tr, targetphase, nodes)
4353 if opts['force']:
4354 if opts['force']:
4354 phases.retractboundary(repo, tr, targetphase, nodes)
4355 phases.retractboundary(repo, tr, targetphase, nodes)
4355 getphase = unfi._phasecache.phase
4356 getphase = unfi._phasecache.phase
4356 newdata = [getphase(unfi, r) for r in unfi]
4357 newdata = [getphase(unfi, r) for r in unfi]
4357 changes = sum(newdata[r] != olddata[r] for r in unfi)
4358 changes = sum(newdata[r] != olddata[r] for r in unfi)
4358 cl = unfi.changelog
4359 cl = unfi.changelog
4359 rejected = [n for n in nodes
4360 rejected = [n for n in nodes
4360 if newdata[cl.rev(n)] < targetphase]
4361 if newdata[cl.rev(n)] < targetphase]
4361 if rejected:
4362 if rejected:
4362 ui.warn(_('cannot move %i changesets to a higher '
4363 ui.warn(_('cannot move %i changesets to a higher '
4363 'phase, use --force\n') % len(rejected))
4364 'phase, use --force\n') % len(rejected))
4364 ret = 1
4365 ret = 1
4365 if changes:
4366 if changes:
4366 msg = _('phase changed for %i changesets\n') % changes
4367 msg = _('phase changed for %i changesets\n') % changes
4367 if ret:
4368 if ret:
4368 ui.status(msg)
4369 ui.status(msg)
4369 else:
4370 else:
4370 ui.note(msg)
4371 ui.note(msg)
4371 else:
4372 else:
4372 ui.warn(_('no phases changed\n'))
4373 ui.warn(_('no phases changed\n'))
4373 return ret
4374 return ret
4374
4375
4375 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4376 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4376 """Run after a changegroup has been added via pull/unbundle
4377 """Run after a changegroup has been added via pull/unbundle
4377
4378
4378 This takes arguments below:
4379 This takes arguments below:
4379
4380
4380 :modheads: change of heads by pull/unbundle
4381 :modheads: change of heads by pull/unbundle
4381 :optupdate: updating working directory is needed or not
4382 :optupdate: updating working directory is needed or not
4382 :checkout: update destination revision (or None to default destination)
4383 :checkout: update destination revision (or None to default destination)
4383 :brev: a name, which might be a bookmark to be activated after updating
4384 :brev: a name, which might be a bookmark to be activated after updating
4384 """
4385 """
4385 if modheads == 0:
4386 if modheads == 0:
4386 return
4387 return
4387 if optupdate:
4388 if optupdate:
4388 try:
4389 try:
4389 return hg.updatetotally(ui, repo, checkout, brev)
4390 return hg.updatetotally(ui, repo, checkout, brev)
4390 except error.UpdateAbort as inst:
4391 except error.UpdateAbort as inst:
4391 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4392 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4392 hint = inst.hint
4393 hint = inst.hint
4393 raise error.UpdateAbort(msg, hint=hint)
4394 raise error.UpdateAbort(msg, hint=hint)
4394 if modheads is not None and modheads > 1:
4395 if modheads is not None and modheads > 1:
4395 currentbranchheads = len(repo.branchheads())
4396 currentbranchheads = len(repo.branchheads())
4396 if currentbranchheads == modheads:
4397 if currentbranchheads == modheads:
4397 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4398 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4398 elif currentbranchheads > 1:
4399 elif currentbranchheads > 1:
4399 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4400 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4400 "merge)\n"))
4401 "merge)\n"))
4401 else:
4402 else:
4402 ui.status(_("(run 'hg heads' to see heads)\n"))
4403 ui.status(_("(run 'hg heads' to see heads)\n"))
4403 elif not ui.configbool('commands', 'update.requiredest'):
4404 elif not ui.configbool('commands', 'update.requiredest'):
4404 ui.status(_("(run 'hg update' to get a working copy)\n"))
4405 ui.status(_("(run 'hg update' to get a working copy)\n"))
4405
4406
4406 @command('pull',
4407 @command('pull',
4407 [('u', 'update', None,
4408 [('u', 'update', None,
4408 _('update to new branch head if new descendants were pulled')),
4409 _('update to new branch head if new descendants were pulled')),
4409 ('f', 'force', None, _('run even when remote repository is unrelated')),
4410 ('f', 'force', None, _('run even when remote repository is unrelated')),
4410 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4411 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4411 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4412 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4412 ('b', 'branch', [], _('a specific branch you would like to pull'),
4413 ('b', 'branch', [], _('a specific branch you would like to pull'),
4413 _('BRANCH')),
4414 _('BRANCH')),
4414 ] + remoteopts,
4415 ] + remoteopts,
4415 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4416 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4416 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4417 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4417 helpbasic=True)
4418 helpbasic=True)
4418 def pull(ui, repo, source="default", **opts):
4419 def pull(ui, repo, source="default", **opts):
4419 """pull changes from the specified source
4420 """pull changes from the specified source
4420
4421
4421 Pull changes from a remote repository to a local one.
4422 Pull changes from a remote repository to a local one.
4422
4423
4423 This finds all changes from the repository at the specified path
4424 This finds all changes from the repository at the specified path
4424 or URL and adds them to a local repository (the current one unless
4425 or URL and adds them to a local repository (the current one unless
4425 -R is specified). By default, this does not update the copy of the
4426 -R is specified). By default, this does not update the copy of the
4426 project in the working directory.
4427 project in the working directory.
4427
4428
4428 When cloning from servers that support it, Mercurial may fetch
4429 When cloning from servers that support it, Mercurial may fetch
4429 pre-generated data. When this is done, hooks operating on incoming
4430 pre-generated data. When this is done, hooks operating on incoming
4430 changesets and changegroups may fire more than once, once for each
4431 changesets and changegroups may fire more than once, once for each
4431 pre-generated bundle and as well as for any additional remaining
4432 pre-generated bundle and as well as for any additional remaining
4432 data. See :hg:`help -e clonebundles` for more.
4433 data. See :hg:`help -e clonebundles` for more.
4433
4434
4434 Use :hg:`incoming` if you want to see what would have been added
4435 Use :hg:`incoming` if you want to see what would have been added
4435 by a pull at the time you issued this command. If you then decide
4436 by a pull at the time you issued this command. If you then decide
4436 to add those changes to the repository, you should use :hg:`pull
4437 to add those changes to the repository, you should use :hg:`pull
4437 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4438 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4438
4439
4439 If SOURCE is omitted, the 'default' path will be used.
4440 If SOURCE is omitted, the 'default' path will be used.
4440 See :hg:`help urls` for more information.
4441 See :hg:`help urls` for more information.
4441
4442
4442 Specifying bookmark as ``.`` is equivalent to specifying the active
4443 Specifying bookmark as ``.`` is equivalent to specifying the active
4443 bookmark's name.
4444 bookmark's name.
4444
4445
4445 Returns 0 on success, 1 if an update had unresolved files.
4446 Returns 0 on success, 1 if an update had unresolved files.
4446 """
4447 """
4447
4448
4448 opts = pycompat.byteskwargs(opts)
4449 opts = pycompat.byteskwargs(opts)
4449 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4450 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4450 msg = _('update destination required by configuration')
4451 msg = _('update destination required by configuration')
4451 hint = _('use hg pull followed by hg update DEST')
4452 hint = _('use hg pull followed by hg update DEST')
4452 raise error.Abort(msg, hint=hint)
4453 raise error.Abort(msg, hint=hint)
4453
4454
4454 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4455 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4455 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4456 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4456 other = hg.peer(repo, opts, source)
4457 other = hg.peer(repo, opts, source)
4457 try:
4458 try:
4458 revs, checkout = hg.addbranchrevs(repo, other, branches,
4459 revs, checkout = hg.addbranchrevs(repo, other, branches,
4459 opts.get('rev'))
4460 opts.get('rev'))
4460
4461
4461 pullopargs = {}
4462 pullopargs = {}
4462
4463
4463 nodes = None
4464 nodes = None
4464 if opts.get('bookmark') or revs:
4465 if opts.get('bookmark') or revs:
4465 # The list of bookmark used here is the same used to actually update
4466 # The list of bookmark used here is the same used to actually update
4466 # the bookmark names, to avoid the race from issue 4689 and we do
4467 # the bookmark names, to avoid the race from issue 4689 and we do
4467 # all lookup and bookmark queries in one go so they see the same
4468 # all lookup and bookmark queries in one go so they see the same
4468 # version of the server state (issue 4700).
4469 # version of the server state (issue 4700).
4469 nodes = []
4470 nodes = []
4470 fnodes = []
4471 fnodes = []
4471 revs = revs or []
4472 revs = revs or []
4472 if revs and not other.capable('lookup'):
4473 if revs and not other.capable('lookup'):
4473 err = _("other repository doesn't support revision lookup, "
4474 err = _("other repository doesn't support revision lookup, "
4474 "so a rev cannot be specified.")
4475 "so a rev cannot be specified.")
4475 raise error.Abort(err)
4476 raise error.Abort(err)
4476 with other.commandexecutor() as e:
4477 with other.commandexecutor() as e:
4477 fremotebookmarks = e.callcommand('listkeys', {
4478 fremotebookmarks = e.callcommand('listkeys', {
4478 'namespace': 'bookmarks'
4479 'namespace': 'bookmarks'
4479 })
4480 })
4480 for r in revs:
4481 for r in revs:
4481 fnodes.append(e.callcommand('lookup', {'key': r}))
4482 fnodes.append(e.callcommand('lookup', {'key': r}))
4482 remotebookmarks = fremotebookmarks.result()
4483 remotebookmarks = fremotebookmarks.result()
4483 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4484 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4484 pullopargs['remotebookmarks'] = remotebookmarks
4485 pullopargs['remotebookmarks'] = remotebookmarks
4485 for b in opts.get('bookmark', []):
4486 for b in opts.get('bookmark', []):
4486 b = repo._bookmarks.expandname(b)
4487 b = repo._bookmarks.expandname(b)
4487 if b not in remotebookmarks:
4488 if b not in remotebookmarks:
4488 raise error.Abort(_('remote bookmark %s not found!') % b)
4489 raise error.Abort(_('remote bookmark %s not found!') % b)
4489 nodes.append(remotebookmarks[b])
4490 nodes.append(remotebookmarks[b])
4490 for i, rev in enumerate(revs):
4491 for i, rev in enumerate(revs):
4491 node = fnodes[i].result()
4492 node = fnodes[i].result()
4492 nodes.append(node)
4493 nodes.append(node)
4493 if rev == checkout:
4494 if rev == checkout:
4494 checkout = node
4495 checkout = node
4495
4496
4496 wlock = util.nullcontextmanager()
4497 wlock = util.nullcontextmanager()
4497 if opts.get('update'):
4498 if opts.get('update'):
4498 wlock = repo.wlock()
4499 wlock = repo.wlock()
4499 with wlock:
4500 with wlock:
4500 pullopargs.update(opts.get('opargs', {}))
4501 pullopargs.update(opts.get('opargs', {}))
4501 modheads = exchange.pull(repo, other, heads=nodes,
4502 modheads = exchange.pull(repo, other, heads=nodes,
4502 force=opts.get('force'),
4503 force=opts.get('force'),
4503 bookmarks=opts.get('bookmark', ()),
4504 bookmarks=opts.get('bookmark', ()),
4504 opargs=pullopargs).cgresult
4505 opargs=pullopargs).cgresult
4505
4506
4506 # brev is a name, which might be a bookmark to be activated at
4507 # brev is a name, which might be a bookmark to be activated at
4507 # the end of the update. In other words, it is an explicit
4508 # the end of the update. In other words, it is an explicit
4508 # destination of the update
4509 # destination of the update
4509 brev = None
4510 brev = None
4510
4511
4511 if checkout:
4512 if checkout:
4512 checkout = repo.unfiltered().changelog.rev(checkout)
4513 checkout = repo.unfiltered().changelog.rev(checkout)
4513
4514
4514 # order below depends on implementation of
4515 # order below depends on implementation of
4515 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4516 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4516 # because 'checkout' is determined without it.
4517 # because 'checkout' is determined without it.
4517 if opts.get('rev'):
4518 if opts.get('rev'):
4518 brev = opts['rev'][0]
4519 brev = opts['rev'][0]
4519 elif opts.get('branch'):
4520 elif opts.get('branch'):
4520 brev = opts['branch'][0]
4521 brev = opts['branch'][0]
4521 else:
4522 else:
4522 brev = branches[0]
4523 brev = branches[0]
4523 repo._subtoppath = source
4524 repo._subtoppath = source
4524 try:
4525 try:
4525 ret = postincoming(ui, repo, modheads, opts.get('update'),
4526 ret = postincoming(ui, repo, modheads, opts.get('update'),
4526 checkout, brev)
4527 checkout, brev)
4527 except error.FilteredRepoLookupError as exc:
4528 except error.FilteredRepoLookupError as exc:
4528 msg = _('cannot update to target: %s') % exc.args[0]
4529 msg = _('cannot update to target: %s') % exc.args[0]
4529 exc.args = (msg,) + exc.args[1:]
4530 exc.args = (msg,) + exc.args[1:]
4530 raise
4531 raise
4531 finally:
4532 finally:
4532 del repo._subtoppath
4533 del repo._subtoppath
4533
4534
4534 finally:
4535 finally:
4535 other.close()
4536 other.close()
4536 return ret
4537 return ret
4537
4538
4538 @command('push',
4539 @command('push',
4539 [('f', 'force', None, _('force push')),
4540 [('f', 'force', None, _('force push')),
4540 ('r', 'rev', [],
4541 ('r', 'rev', [],
4541 _('a changeset intended to be included in the destination'),
4542 _('a changeset intended to be included in the destination'),
4542 _('REV')),
4543 _('REV')),
4543 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4544 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4544 ('b', 'branch', [],
4545 ('b', 'branch', [],
4545 _('a specific branch you would like to push'), _('BRANCH')),
4546 _('a specific branch you would like to push'), _('BRANCH')),
4546 ('', 'new-branch', False, _('allow pushing a new branch')),
4547 ('', 'new-branch', False, _('allow pushing a new branch')),
4547 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4548 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4548 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4549 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4549 ] + remoteopts,
4550 ] + remoteopts,
4550 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4551 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4551 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4552 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4552 helpbasic=True)
4553 helpbasic=True)
4553 def push(ui, repo, dest=None, **opts):
4554 def push(ui, repo, dest=None, **opts):
4554 """push changes to the specified destination
4555 """push changes to the specified destination
4555
4556
4556 Push changesets from the local repository to the specified
4557 Push changesets from the local repository to the specified
4557 destination.
4558 destination.
4558
4559
4559 This operation is symmetrical to pull: it is identical to a pull
4560 This operation is symmetrical to pull: it is identical to a pull
4560 in the destination repository from the current one.
4561 in the destination repository from the current one.
4561
4562
4562 By default, push will not allow creation of new heads at the
4563 By default, push will not allow creation of new heads at the
4563 destination, since multiple heads would make it unclear which head
4564 destination, since multiple heads would make it unclear which head
4564 to use. In this situation, it is recommended to pull and merge
4565 to use. In this situation, it is recommended to pull and merge
4565 before pushing.
4566 before pushing.
4566
4567
4567 Use --new-branch if you want to allow push to create a new named
4568 Use --new-branch if you want to allow push to create a new named
4568 branch that is not present at the destination. This allows you to
4569 branch that is not present at the destination. This allows you to
4569 only create a new branch without forcing other changes.
4570 only create a new branch without forcing other changes.
4570
4571
4571 .. note::
4572 .. note::
4572
4573
4573 Extra care should be taken with the -f/--force option,
4574 Extra care should be taken with the -f/--force option,
4574 which will push all new heads on all branches, an action which will
4575 which will push all new heads on all branches, an action which will
4575 almost always cause confusion for collaborators.
4576 almost always cause confusion for collaborators.
4576
4577
4577 If -r/--rev is used, the specified revision and all its ancestors
4578 If -r/--rev is used, the specified revision and all its ancestors
4578 will be pushed to the remote repository.
4579 will be pushed to the remote repository.
4579
4580
4580 If -B/--bookmark is used, the specified bookmarked revision, its
4581 If -B/--bookmark is used, the specified bookmarked revision, its
4581 ancestors, and the bookmark will be pushed to the remote
4582 ancestors, and the bookmark will be pushed to the remote
4582 repository. Specifying ``.`` is equivalent to specifying the active
4583 repository. Specifying ``.`` is equivalent to specifying the active
4583 bookmark's name.
4584 bookmark's name.
4584
4585
4585 Please see :hg:`help urls` for important details about ``ssh://``
4586 Please see :hg:`help urls` for important details about ``ssh://``
4586 URLs. If DESTINATION is omitted, a default path will be used.
4587 URLs. If DESTINATION is omitted, a default path will be used.
4587
4588
4588 .. container:: verbose
4589 .. container:: verbose
4589
4590
4590 The --pushvars option sends strings to the server that become
4591 The --pushvars option sends strings to the server that become
4591 environment variables prepended with ``HG_USERVAR_``. For example,
4592 environment variables prepended with ``HG_USERVAR_``. For example,
4592 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4593 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4593 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4594 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4594
4595
4595 pushvars can provide for user-overridable hooks as well as set debug
4596 pushvars can provide for user-overridable hooks as well as set debug
4596 levels. One example is having a hook that blocks commits containing
4597 levels. One example is having a hook that blocks commits containing
4597 conflict markers, but enables the user to override the hook if the file
4598 conflict markers, but enables the user to override the hook if the file
4598 is using conflict markers for testing purposes or the file format has
4599 is using conflict markers for testing purposes or the file format has
4599 strings that look like conflict markers.
4600 strings that look like conflict markers.
4600
4601
4601 By default, servers will ignore `--pushvars`. To enable it add the
4602 By default, servers will ignore `--pushvars`. To enable it add the
4602 following to your configuration file::
4603 following to your configuration file::
4603
4604
4604 [push]
4605 [push]
4605 pushvars.server = true
4606 pushvars.server = true
4606
4607
4607 Returns 0 if push was successful, 1 if nothing to push.
4608 Returns 0 if push was successful, 1 if nothing to push.
4608 """
4609 """
4609
4610
4610 opts = pycompat.byteskwargs(opts)
4611 opts = pycompat.byteskwargs(opts)
4611 if opts.get('bookmark'):
4612 if opts.get('bookmark'):
4612 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4613 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4613 for b in opts['bookmark']:
4614 for b in opts['bookmark']:
4614 # translate -B options to -r so changesets get pushed
4615 # translate -B options to -r so changesets get pushed
4615 b = repo._bookmarks.expandname(b)
4616 b = repo._bookmarks.expandname(b)
4616 if b in repo._bookmarks:
4617 if b in repo._bookmarks:
4617 opts.setdefault('rev', []).append(b)
4618 opts.setdefault('rev', []).append(b)
4618 else:
4619 else:
4619 # if we try to push a deleted bookmark, translate it to null
4620 # if we try to push a deleted bookmark, translate it to null
4620 # this lets simultaneous -r, -b options continue working
4621 # this lets simultaneous -r, -b options continue working
4621 opts.setdefault('rev', []).append("null")
4622 opts.setdefault('rev', []).append("null")
4622
4623
4623 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4624 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4624 if not path:
4625 if not path:
4625 raise error.Abort(_('default repository not configured!'),
4626 raise error.Abort(_('default repository not configured!'),
4626 hint=_("see 'hg help config.paths'"))
4627 hint=_("see 'hg help config.paths'"))
4627 dest = path.pushloc or path.loc
4628 dest = path.pushloc or path.loc
4628 branches = (path.branch, opts.get('branch') or [])
4629 branches = (path.branch, opts.get('branch') or [])
4629 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4630 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4630 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4631 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4631 other = hg.peer(repo, opts, dest)
4632 other = hg.peer(repo, opts, dest)
4632
4633
4633 if revs:
4634 if revs:
4634 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4635 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4635 if not revs:
4636 if not revs:
4636 raise error.Abort(_("specified revisions evaluate to an empty set"),
4637 raise error.Abort(_("specified revisions evaluate to an empty set"),
4637 hint=_("use different revision arguments"))
4638 hint=_("use different revision arguments"))
4638 elif path.pushrev:
4639 elif path.pushrev:
4639 # It doesn't make any sense to specify ancestor revisions. So limit
4640 # It doesn't make any sense to specify ancestor revisions. So limit
4640 # to DAG heads to make discovery simpler.
4641 # to DAG heads to make discovery simpler.
4641 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4642 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4642 revs = scmutil.revrange(repo, [expr])
4643 revs = scmutil.revrange(repo, [expr])
4643 revs = [repo[rev].node() for rev in revs]
4644 revs = [repo[rev].node() for rev in revs]
4644 if not revs:
4645 if not revs:
4645 raise error.Abort(_('default push revset for path evaluates to an '
4646 raise error.Abort(_('default push revset for path evaluates to an '
4646 'empty set'))
4647 'empty set'))
4647
4648
4648 repo._subtoppath = dest
4649 repo._subtoppath = dest
4649 try:
4650 try:
4650 # push subrepos depth-first for coherent ordering
4651 # push subrepos depth-first for coherent ordering
4651 c = repo['.']
4652 c = repo['.']
4652 subs = c.substate # only repos that are committed
4653 subs = c.substate # only repos that are committed
4653 for s in sorted(subs):
4654 for s in sorted(subs):
4654 result = c.sub(s).push(opts)
4655 result = c.sub(s).push(opts)
4655 if result == 0:
4656 if result == 0:
4656 return not result
4657 return not result
4657 finally:
4658 finally:
4658 del repo._subtoppath
4659 del repo._subtoppath
4659
4660
4660 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4661 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4661 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4662 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4662
4663
4663 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4664 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4664 newbranch=opts.get('new_branch'),
4665 newbranch=opts.get('new_branch'),
4665 bookmarks=opts.get('bookmark', ()),
4666 bookmarks=opts.get('bookmark', ()),
4666 publish=opts.get('publish'),
4667 publish=opts.get('publish'),
4667 opargs=opargs)
4668 opargs=opargs)
4668
4669
4669 result = not pushop.cgresult
4670 result = not pushop.cgresult
4670
4671
4671 if pushop.bkresult is not None:
4672 if pushop.bkresult is not None:
4672 if pushop.bkresult == 2:
4673 if pushop.bkresult == 2:
4673 result = 2
4674 result = 2
4674 elif not result and pushop.bkresult:
4675 elif not result and pushop.bkresult:
4675 result = 2
4676 result = 2
4676
4677
4677 return result
4678 return result
4678
4679
4679 @command('recover',
4680 @command('recover',
4680 [('','verify', True, "run `hg verify` after succesful recover"),
4681 [('','verify', True, "run `hg verify` after succesful recover"),
4681 ],
4682 ],
4682 helpcategory=command.CATEGORY_MAINTENANCE)
4683 helpcategory=command.CATEGORY_MAINTENANCE)
4683 def recover(ui, repo, **opts):
4684 def recover(ui, repo, **opts):
4684 """roll back an interrupted transaction
4685 """roll back an interrupted transaction
4685
4686
4686 Recover from an interrupted commit or pull.
4687 Recover from an interrupted commit or pull.
4687
4688
4688 This command tries to fix the repository status after an
4689 This command tries to fix the repository status after an
4689 interrupted operation. It should only be necessary when Mercurial
4690 interrupted operation. It should only be necessary when Mercurial
4690 suggests it.
4691 suggests it.
4691
4692
4692 Returns 0 if successful, 1 if nothing to recover or verify fails.
4693 Returns 0 if successful, 1 if nothing to recover or verify fails.
4693 """
4694 """
4694 ret = repo.recover()
4695 ret = repo.recover()
4695 if ret:
4696 if ret:
4696 if opts[r'verify']:
4697 if opts[r'verify']:
4697 return hg.verify(repo)
4698 return hg.verify(repo)
4698 else:
4699 else:
4699 msg = _("(verify step skipped, run `hg verify` to check your "
4700 msg = _("(verify step skipped, run `hg verify` to check your "
4700 "repository content)\n")
4701 "repository content)\n")
4701 ui.warn(msg)
4702 ui.warn(msg)
4702 return 0
4703 return 0
4703 return 1
4704 return 1
4704
4705
4705 @command('remove|rm',
4706 @command('remove|rm',
4706 [('A', 'after', None, _('record delete for missing files')),
4707 [('A', 'after', None, _('record delete for missing files')),
4707 ('f', 'force', None,
4708 ('f', 'force', None,
4708 _('forget added files, delete modified files')),
4709 _('forget added files, delete modified files')),
4709 ] + subrepoopts + walkopts + dryrunopts,
4710 ] + subrepoopts + walkopts + dryrunopts,
4710 _('[OPTION]... FILE...'),
4711 _('[OPTION]... FILE...'),
4711 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4712 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4712 helpbasic=True, inferrepo=True)
4713 helpbasic=True, inferrepo=True)
4713 def remove(ui, repo, *pats, **opts):
4714 def remove(ui, repo, *pats, **opts):
4714 """remove the specified files on the next commit
4715 """remove the specified files on the next commit
4715
4716
4716 Schedule the indicated files for removal from the current branch.
4717 Schedule the indicated files for removal from the current branch.
4717
4718
4718 This command schedules the files to be removed at the next commit.
4719 This command schedules the files to be removed at the next commit.
4719 To undo a remove before that, see :hg:`revert`. To undo added
4720 To undo a remove before that, see :hg:`revert`. To undo added
4720 files, see :hg:`forget`.
4721 files, see :hg:`forget`.
4721
4722
4722 .. container:: verbose
4723 .. container:: verbose
4723
4724
4724 -A/--after can be used to remove only files that have already
4725 -A/--after can be used to remove only files that have already
4725 been deleted, -f/--force can be used to force deletion, and -Af
4726 been deleted, -f/--force can be used to force deletion, and -Af
4726 can be used to remove files from the next revision without
4727 can be used to remove files from the next revision without
4727 deleting them from the working directory.
4728 deleting them from the working directory.
4728
4729
4729 The following table details the behavior of remove for different
4730 The following table details the behavior of remove for different
4730 file states (columns) and option combinations (rows). The file
4731 file states (columns) and option combinations (rows). The file
4731 states are Added [A], Clean [C], Modified [M] and Missing [!]
4732 states are Added [A], Clean [C], Modified [M] and Missing [!]
4732 (as reported by :hg:`status`). The actions are Warn, Remove
4733 (as reported by :hg:`status`). The actions are Warn, Remove
4733 (from branch) and Delete (from disk):
4734 (from branch) and Delete (from disk):
4734
4735
4735 ========= == == == ==
4736 ========= == == == ==
4736 opt/state A C M !
4737 opt/state A C M !
4737 ========= == == == ==
4738 ========= == == == ==
4738 none W RD W R
4739 none W RD W R
4739 -f R RD RD R
4740 -f R RD RD R
4740 -A W W W R
4741 -A W W W R
4741 -Af R R R R
4742 -Af R R R R
4742 ========= == == == ==
4743 ========= == == == ==
4743
4744
4744 .. note::
4745 .. note::
4745
4746
4746 :hg:`remove` never deletes files in Added [A] state from the
4747 :hg:`remove` never deletes files in Added [A] state from the
4747 working directory, not even if ``--force`` is specified.
4748 working directory, not even if ``--force`` is specified.
4748
4749
4749 Returns 0 on success, 1 if any warnings encountered.
4750 Returns 0 on success, 1 if any warnings encountered.
4750 """
4751 """
4751
4752
4752 opts = pycompat.byteskwargs(opts)
4753 opts = pycompat.byteskwargs(opts)
4753 after, force = opts.get('after'), opts.get('force')
4754 after, force = opts.get('after'), opts.get('force')
4754 dryrun = opts.get('dry_run')
4755 dryrun = opts.get('dry_run')
4755 if not pats and not after:
4756 if not pats and not after:
4756 raise error.Abort(_('no files specified'))
4757 raise error.Abort(_('no files specified'))
4757
4758
4758 m = scmutil.match(repo[None], pats, opts)
4759 m = scmutil.match(repo[None], pats, opts)
4759 subrepos = opts.get('subrepos')
4760 subrepos = opts.get('subrepos')
4760 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4761 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4761 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4762 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4762 dryrun=dryrun)
4763 dryrun=dryrun)
4763
4764
4764 @command('rename|move|mv',
4765 @command('rename|move|mv',
4765 [('A', 'after', None, _('record a rename that has already occurred')),
4766 [('A', 'after', None, _('record a rename that has already occurred')),
4766 ('f', 'force', None, _('forcibly move over an existing managed file')),
4767 ('f', 'force', None, _('forcibly move over an existing managed file')),
4767 ] + walkopts + dryrunopts,
4768 ] + walkopts + dryrunopts,
4768 _('[OPTION]... SOURCE... DEST'),
4769 _('[OPTION]... SOURCE... DEST'),
4769 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4770 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4770 def rename(ui, repo, *pats, **opts):
4771 def rename(ui, repo, *pats, **opts):
4771 """rename files; equivalent of copy + remove
4772 """rename files; equivalent of copy + remove
4772
4773
4773 Mark dest as copies of sources; mark sources for deletion. If dest
4774 Mark dest as copies of sources; mark sources for deletion. If dest
4774 is a directory, copies are put in that directory. If dest is a
4775 is a directory, copies are put in that directory. If dest is a
4775 file, there can only be one source.
4776 file, there can only be one source.
4776
4777
4777 By default, this command copies the contents of files as they
4778 By default, this command copies the contents of files as they
4778 exist in the working directory. If invoked with -A/--after, the
4779 exist in the working directory. If invoked with -A/--after, the
4779 operation is recorded, but no copying is performed.
4780 operation is recorded, but no copying is performed.
4780
4781
4781 This command takes effect at the next commit. To undo a rename
4782 This command takes effect at the next commit. To undo a rename
4782 before that, see :hg:`revert`.
4783 before that, see :hg:`revert`.
4783
4784
4784 Returns 0 on success, 1 if errors are encountered.
4785 Returns 0 on success, 1 if errors are encountered.
4785 """
4786 """
4786 opts = pycompat.byteskwargs(opts)
4787 opts = pycompat.byteskwargs(opts)
4787 with repo.wlock(False):
4788 with repo.wlock(False):
4788 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4789 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4789
4790
4790 @command('resolve',
4791 @command('resolve',
4791 [('a', 'all', None, _('select all unresolved files')),
4792 [('a', 'all', None, _('select all unresolved files')),
4792 ('l', 'list', None, _('list state of files needing merge')),
4793 ('l', 'list', None, _('list state of files needing merge')),
4793 ('m', 'mark', None, _('mark files as resolved')),
4794 ('m', 'mark', None, _('mark files as resolved')),
4794 ('u', 'unmark', None, _('mark files as unresolved')),
4795 ('u', 'unmark', None, _('mark files as unresolved')),
4795 ('n', 'no-status', None, _('hide status prefix')),
4796 ('n', 'no-status', None, _('hide status prefix')),
4796 ('', 're-merge', None, _('re-merge files'))]
4797 ('', 're-merge', None, _('re-merge files'))]
4797 + mergetoolopts + walkopts + formatteropts,
4798 + mergetoolopts + walkopts + formatteropts,
4798 _('[OPTION]... [FILE]...'),
4799 _('[OPTION]... [FILE]...'),
4799 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4800 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4800 inferrepo=True)
4801 inferrepo=True)
4801 def resolve(ui, repo, *pats, **opts):
4802 def resolve(ui, repo, *pats, **opts):
4802 """redo merges or set/view the merge status of files
4803 """redo merges or set/view the merge status of files
4803
4804
4804 Merges with unresolved conflicts are often the result of
4805 Merges with unresolved conflicts are often the result of
4805 non-interactive merging using the ``internal:merge`` configuration
4806 non-interactive merging using the ``internal:merge`` configuration
4806 setting, or a command-line merge tool like ``diff3``. The resolve
4807 setting, or a command-line merge tool like ``diff3``. The resolve
4807 command is used to manage the files involved in a merge, after
4808 command is used to manage the files involved in a merge, after
4808 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4809 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4809 working directory must have two parents). See :hg:`help
4810 working directory must have two parents). See :hg:`help
4810 merge-tools` for information on configuring merge tools.
4811 merge-tools` for information on configuring merge tools.
4811
4812
4812 The resolve command can be used in the following ways:
4813 The resolve command can be used in the following ways:
4813
4814
4814 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4815 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4815 the specified files, discarding any previous merge attempts. Re-merging
4816 the specified files, discarding any previous merge attempts. Re-merging
4816 is not performed for files already marked as resolved. Use ``--all/-a``
4817 is not performed for files already marked as resolved. Use ``--all/-a``
4817 to select all unresolved files. ``--tool`` can be used to specify
4818 to select all unresolved files. ``--tool`` can be used to specify
4818 the merge tool used for the given files. It overrides the HGMERGE
4819 the merge tool used for the given files. It overrides the HGMERGE
4819 environment variable and your configuration files. Previous file
4820 environment variable and your configuration files. Previous file
4820 contents are saved with a ``.orig`` suffix.
4821 contents are saved with a ``.orig`` suffix.
4821
4822
4822 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4823 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4823 (e.g. after having manually fixed-up the files). The default is
4824 (e.g. after having manually fixed-up the files). The default is
4824 to mark all unresolved files.
4825 to mark all unresolved files.
4825
4826
4826 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4827 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4827 default is to mark all resolved files.
4828 default is to mark all resolved files.
4828
4829
4829 - :hg:`resolve -l`: list files which had or still have conflicts.
4830 - :hg:`resolve -l`: list files which had or still have conflicts.
4830 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4831 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4831 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4832 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4832 the list. See :hg:`help filesets` for details.
4833 the list. See :hg:`help filesets` for details.
4833
4834
4834 .. note::
4835 .. note::
4835
4836
4836 Mercurial will not let you commit files with unresolved merge
4837 Mercurial will not let you commit files with unresolved merge
4837 conflicts. You must use :hg:`resolve -m ...` before you can
4838 conflicts. You must use :hg:`resolve -m ...` before you can
4838 commit after a conflicting merge.
4839 commit after a conflicting merge.
4839
4840
4840 .. container:: verbose
4841 .. container:: verbose
4841
4842
4842 Template:
4843 Template:
4843
4844
4844 The following keywords are supported in addition to the common template
4845 The following keywords are supported in addition to the common template
4845 keywords and functions. See also :hg:`help templates`.
4846 keywords and functions. See also :hg:`help templates`.
4846
4847
4847 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4848 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4848 :path: String. Repository-absolute path of the file.
4849 :path: String. Repository-absolute path of the file.
4849
4850
4850 Returns 0 on success, 1 if any files fail a resolve attempt.
4851 Returns 0 on success, 1 if any files fail a resolve attempt.
4851 """
4852 """
4852
4853
4853 opts = pycompat.byteskwargs(opts)
4854 opts = pycompat.byteskwargs(opts)
4854 confirm = ui.configbool('commands', 'resolve.confirm')
4855 confirm = ui.configbool('commands', 'resolve.confirm')
4855 flaglist = 'all mark unmark list no_status re_merge'.split()
4856 flaglist = 'all mark unmark list no_status re_merge'.split()
4856 all, mark, unmark, show, nostatus, remerge = [
4857 all, mark, unmark, show, nostatus, remerge = [
4857 opts.get(o) for o in flaglist]
4858 opts.get(o) for o in flaglist]
4858
4859
4859 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4860 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4860 if actioncount > 1:
4861 if actioncount > 1:
4861 raise error.Abort(_("too many actions specified"))
4862 raise error.Abort(_("too many actions specified"))
4862 elif (actioncount == 0
4863 elif (actioncount == 0
4863 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4864 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4864 hint = _('use --mark, --unmark, --list or --re-merge')
4865 hint = _('use --mark, --unmark, --list or --re-merge')
4865 raise error.Abort(_('no action specified'), hint=hint)
4866 raise error.Abort(_('no action specified'), hint=hint)
4866 if pats and all:
4867 if pats and all:
4867 raise error.Abort(_("can't specify --all and patterns"))
4868 raise error.Abort(_("can't specify --all and patterns"))
4868 if not (all or pats or show or mark or unmark):
4869 if not (all or pats or show or mark or unmark):
4869 raise error.Abort(_('no files or directories specified'),
4870 raise error.Abort(_('no files or directories specified'),
4870 hint=('use --all to re-merge all unresolved files'))
4871 hint=('use --all to re-merge all unresolved files'))
4871
4872
4872 if confirm:
4873 if confirm:
4873 if all:
4874 if all:
4874 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4875 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4875 b'$$ &Yes $$ &No')):
4876 b'$$ &Yes $$ &No')):
4876 raise error.Abort(_('user quit'))
4877 raise error.Abort(_('user quit'))
4877 if mark and not pats:
4878 if mark and not pats:
4878 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4879 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4879 b'$$ &Yes $$ &No')):
4880 b'$$ &Yes $$ &No')):
4880 raise error.Abort(_('user quit'))
4881 raise error.Abort(_('user quit'))
4881 if unmark and not pats:
4882 if unmark and not pats:
4882 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4883 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4883 b'$$ &Yes $$ &No')):
4884 b'$$ &Yes $$ &No')):
4884 raise error.Abort(_('user quit'))
4885 raise error.Abort(_('user quit'))
4885
4886
4886 uipathfn = scmutil.getuipathfn(repo)
4887 uipathfn = scmutil.getuipathfn(repo)
4887
4888
4888 if show:
4889 if show:
4889 ui.pager('resolve')
4890 ui.pager('resolve')
4890 fm = ui.formatter('resolve', opts)
4891 fm = ui.formatter('resolve', opts)
4891 ms = mergemod.mergestate.read(repo)
4892 ms = mergemod.mergestate.read(repo)
4892 wctx = repo[None]
4893 wctx = repo[None]
4893 m = scmutil.match(wctx, pats, opts)
4894 m = scmutil.match(wctx, pats, opts)
4894
4895
4895 # Labels and keys based on merge state. Unresolved path conflicts show
4896 # Labels and keys based on merge state. Unresolved path conflicts show
4896 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4897 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4897 # resolved conflicts.
4898 # resolved conflicts.
4898 mergestateinfo = {
4899 mergestateinfo = {
4899 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4900 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4900 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4901 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4901 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4902 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4902 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4903 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4903 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4904 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4904 'D'),
4905 'D'),
4905 }
4906 }
4906
4907
4907 for f in ms:
4908 for f in ms:
4908 if not m(f):
4909 if not m(f):
4909 continue
4910 continue
4910
4911
4911 label, key = mergestateinfo[ms[f]]
4912 label, key = mergestateinfo[ms[f]]
4912 fm.startitem()
4913 fm.startitem()
4913 fm.context(ctx=wctx)
4914 fm.context(ctx=wctx)
4914 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4915 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4915 fm.data(path=f)
4916 fm.data(path=f)
4916 fm.plain('%s\n' % uipathfn(f), label=label)
4917 fm.plain('%s\n' % uipathfn(f), label=label)
4917 fm.end()
4918 fm.end()
4918 return 0
4919 return 0
4919
4920
4920 with repo.wlock():
4921 with repo.wlock():
4921 ms = mergemod.mergestate.read(repo)
4922 ms = mergemod.mergestate.read(repo)
4922
4923
4923 if not (ms.active() or repo.dirstate.p2() != nullid):
4924 if not (ms.active() or repo.dirstate.p2() != nullid):
4924 raise error.Abort(
4925 raise error.Abort(
4925 _('resolve command not applicable when not merging'))
4926 _('resolve command not applicable when not merging'))
4926
4927
4927 wctx = repo[None]
4928 wctx = repo[None]
4928
4929
4929 if (ms.mergedriver
4930 if (ms.mergedriver
4930 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4931 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4931 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4932 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4932 ms.commit()
4933 ms.commit()
4933 # allow mark and unmark to go through
4934 # allow mark and unmark to go through
4934 if not mark and not unmark and not proceed:
4935 if not mark and not unmark and not proceed:
4935 return 1
4936 return 1
4936
4937
4937 m = scmutil.match(wctx, pats, opts)
4938 m = scmutil.match(wctx, pats, opts)
4938 ret = 0
4939 ret = 0
4939 didwork = False
4940 didwork = False
4940 runconclude = False
4941 runconclude = False
4941
4942
4942 tocomplete = []
4943 tocomplete = []
4943 hasconflictmarkers = []
4944 hasconflictmarkers = []
4944 if mark:
4945 if mark:
4945 markcheck = ui.config('commands', 'resolve.mark-check')
4946 markcheck = ui.config('commands', 'resolve.mark-check')
4946 if markcheck not in ['warn', 'abort']:
4947 if markcheck not in ['warn', 'abort']:
4947 # Treat all invalid / unrecognized values as 'none'.
4948 # Treat all invalid / unrecognized values as 'none'.
4948 markcheck = False
4949 markcheck = False
4949 for f in ms:
4950 for f in ms:
4950 if not m(f):
4951 if not m(f):
4951 continue
4952 continue
4952
4953
4953 didwork = True
4954 didwork = True
4954
4955
4955 # don't let driver-resolved files be marked, and run the conclude
4956 # don't let driver-resolved files be marked, and run the conclude
4956 # step if asked to resolve
4957 # step if asked to resolve
4957 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4958 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4958 exact = m.exact(f)
4959 exact = m.exact(f)
4959 if mark:
4960 if mark:
4960 if exact:
4961 if exact:
4961 ui.warn(_('not marking %s as it is driver-resolved\n')
4962 ui.warn(_('not marking %s as it is driver-resolved\n')
4962 % uipathfn(f))
4963 % uipathfn(f))
4963 elif unmark:
4964 elif unmark:
4964 if exact:
4965 if exact:
4965 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4966 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4966 % uipathfn(f))
4967 % uipathfn(f))
4967 else:
4968 else:
4968 runconclude = True
4969 runconclude = True
4969 continue
4970 continue
4970
4971
4971 # path conflicts must be resolved manually
4972 # path conflicts must be resolved manually
4972 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4973 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4973 mergemod.MERGE_RECORD_RESOLVED_PATH):
4974 mergemod.MERGE_RECORD_RESOLVED_PATH):
4974 if mark:
4975 if mark:
4975 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4976 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4976 elif unmark:
4977 elif unmark:
4977 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4978 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4978 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4979 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4979 ui.warn(_('%s: path conflict must be resolved manually\n')
4980 ui.warn(_('%s: path conflict must be resolved manually\n')
4980 % uipathfn(f))
4981 % uipathfn(f))
4981 continue
4982 continue
4982
4983
4983 if mark:
4984 if mark:
4984 if markcheck:
4985 if markcheck:
4985 fdata = repo.wvfs.tryread(f)
4986 fdata = repo.wvfs.tryread(f)
4986 if (filemerge.hasconflictmarkers(fdata) and
4987 if (filemerge.hasconflictmarkers(fdata) and
4987 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4988 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4988 hasconflictmarkers.append(f)
4989 hasconflictmarkers.append(f)
4989 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4990 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4990 elif unmark:
4991 elif unmark:
4991 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4992 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4992 else:
4993 else:
4993 # backup pre-resolve (merge uses .orig for its own purposes)
4994 # backup pre-resolve (merge uses .orig for its own purposes)
4994 a = repo.wjoin(f)
4995 a = repo.wjoin(f)
4995 try:
4996 try:
4996 util.copyfile(a, a + ".resolve")
4997 util.copyfile(a, a + ".resolve")
4997 except (IOError, OSError) as inst:
4998 except (IOError, OSError) as inst:
4998 if inst.errno != errno.ENOENT:
4999 if inst.errno != errno.ENOENT:
4999 raise
5000 raise
5000
5001
5001 try:
5002 try:
5002 # preresolve file
5003 # preresolve file
5003 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5004 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5004 with ui.configoverride(overrides, 'resolve'):
5005 with ui.configoverride(overrides, 'resolve'):
5005 complete, r = ms.preresolve(f, wctx)
5006 complete, r = ms.preresolve(f, wctx)
5006 if not complete:
5007 if not complete:
5007 tocomplete.append(f)
5008 tocomplete.append(f)
5008 elif r:
5009 elif r:
5009 ret = 1
5010 ret = 1
5010 finally:
5011 finally:
5011 ms.commit()
5012 ms.commit()
5012
5013
5013 # replace filemerge's .orig file with our resolve file, but only
5014 # replace filemerge's .orig file with our resolve file, but only
5014 # for merges that are complete
5015 # for merges that are complete
5015 if complete:
5016 if complete:
5016 try:
5017 try:
5017 util.rename(a + ".resolve",
5018 util.rename(a + ".resolve",
5018 scmutil.backuppath(ui, repo, f))
5019 scmutil.backuppath(ui, repo, f))
5019 except OSError as inst:
5020 except OSError as inst:
5020 if inst.errno != errno.ENOENT:
5021 if inst.errno != errno.ENOENT:
5021 raise
5022 raise
5022
5023
5023 if hasconflictmarkers:
5024 if hasconflictmarkers:
5024 ui.warn(_('warning: the following files still have conflict '
5025 ui.warn(_('warning: the following files still have conflict '
5025 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5026 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5026 for f in hasconflictmarkers))
5027 for f in hasconflictmarkers))
5027 if markcheck == 'abort' and not all and not pats:
5028 if markcheck == 'abort' and not all and not pats:
5028 raise error.Abort(_('conflict markers detected'),
5029 raise error.Abort(_('conflict markers detected'),
5029 hint=_('use --all to mark anyway'))
5030 hint=_('use --all to mark anyway'))
5030
5031
5031 for f in tocomplete:
5032 for f in tocomplete:
5032 try:
5033 try:
5033 # resolve file
5034 # resolve file
5034 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5035 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5035 with ui.configoverride(overrides, 'resolve'):
5036 with ui.configoverride(overrides, 'resolve'):
5036 r = ms.resolve(f, wctx)
5037 r = ms.resolve(f, wctx)
5037 if r:
5038 if r:
5038 ret = 1
5039 ret = 1
5039 finally:
5040 finally:
5040 ms.commit()
5041 ms.commit()
5041
5042
5042 # replace filemerge's .orig file with our resolve file
5043 # replace filemerge's .orig file with our resolve file
5043 a = repo.wjoin(f)
5044 a = repo.wjoin(f)
5044 try:
5045 try:
5045 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5046 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5046 except OSError as inst:
5047 except OSError as inst:
5047 if inst.errno != errno.ENOENT:
5048 if inst.errno != errno.ENOENT:
5048 raise
5049 raise
5049
5050
5050 ms.commit()
5051 ms.commit()
5051 ms.recordactions()
5052 ms.recordactions()
5052
5053
5053 if not didwork and pats:
5054 if not didwork and pats:
5054 hint = None
5055 hint = None
5055 if not any([p for p in pats if p.find(':') >= 0]):
5056 if not any([p for p in pats if p.find(':') >= 0]):
5056 pats = ['path:%s' % p for p in pats]
5057 pats = ['path:%s' % p for p in pats]
5057 m = scmutil.match(wctx, pats, opts)
5058 m = scmutil.match(wctx, pats, opts)
5058 for f in ms:
5059 for f in ms:
5059 if not m(f):
5060 if not m(f):
5060 continue
5061 continue
5061 def flag(o):
5062 def flag(o):
5062 if o == 're_merge':
5063 if o == 're_merge':
5063 return '--re-merge '
5064 return '--re-merge '
5064 return '-%s ' % o[0:1]
5065 return '-%s ' % o[0:1]
5065 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5066 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5066 hint = _("(try: hg resolve %s%s)\n") % (
5067 hint = _("(try: hg resolve %s%s)\n") % (
5067 flags,
5068 flags,
5068 ' '.join(pats))
5069 ' '.join(pats))
5069 break
5070 break
5070 ui.warn(_("arguments do not match paths that need resolving\n"))
5071 ui.warn(_("arguments do not match paths that need resolving\n"))
5071 if hint:
5072 if hint:
5072 ui.warn(hint)
5073 ui.warn(hint)
5073 elif ms.mergedriver and ms.mdstate() != 's':
5074 elif ms.mergedriver and ms.mdstate() != 's':
5074 # run conclude step when either a driver-resolved file is requested
5075 # run conclude step when either a driver-resolved file is requested
5075 # or there are no driver-resolved files
5076 # or there are no driver-resolved files
5076 # we can't use 'ret' to determine whether any files are unresolved
5077 # we can't use 'ret' to determine whether any files are unresolved
5077 # because we might not have tried to resolve some
5078 # because we might not have tried to resolve some
5078 if ((runconclude or not list(ms.driverresolved()))
5079 if ((runconclude or not list(ms.driverresolved()))
5079 and not list(ms.unresolved())):
5080 and not list(ms.unresolved())):
5080 proceed = mergemod.driverconclude(repo, ms, wctx)
5081 proceed = mergemod.driverconclude(repo, ms, wctx)
5081 ms.commit()
5082 ms.commit()
5082 if not proceed:
5083 if not proceed:
5083 return 1
5084 return 1
5084
5085
5085 # Nudge users into finishing an unfinished operation
5086 # Nudge users into finishing an unfinished operation
5086 unresolvedf = list(ms.unresolved())
5087 unresolvedf = list(ms.unresolved())
5087 driverresolvedf = list(ms.driverresolved())
5088 driverresolvedf = list(ms.driverresolved())
5088 if not unresolvedf and not driverresolvedf:
5089 if not unresolvedf and not driverresolvedf:
5089 ui.status(_('(no more unresolved files)\n'))
5090 ui.status(_('(no more unresolved files)\n'))
5090 cmdutil.checkafterresolved(repo)
5091 cmdutil.checkafterresolved(repo)
5091 elif not unresolvedf:
5092 elif not unresolvedf:
5092 ui.status(_('(no more unresolved files -- '
5093 ui.status(_('(no more unresolved files -- '
5093 'run "hg resolve --all" to conclude)\n'))
5094 'run "hg resolve --all" to conclude)\n'))
5094
5095
5095 return ret
5096 return ret
5096
5097
5097 @command('revert',
5098 @command('revert',
5098 [('a', 'all', None, _('revert all changes when no arguments given')),
5099 [('a', 'all', None, _('revert all changes when no arguments given')),
5099 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5100 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5100 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5101 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5101 ('C', 'no-backup', None, _('do not save backup copies of files')),
5102 ('C', 'no-backup', None, _('do not save backup copies of files')),
5102 ('i', 'interactive', None, _('interactively select the changes')),
5103 ('i', 'interactive', None, _('interactively select the changes')),
5103 ] + walkopts + dryrunopts,
5104 ] + walkopts + dryrunopts,
5104 _('[OPTION]... [-r REV] [NAME]...'),
5105 _('[OPTION]... [-r REV] [NAME]...'),
5105 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5106 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5106 def revert(ui, repo, *pats, **opts):
5107 def revert(ui, repo, *pats, **opts):
5107 """restore files to their checkout state
5108 """restore files to their checkout state
5108
5109
5109 .. note::
5110 .. note::
5110
5111
5111 To check out earlier revisions, you should use :hg:`update REV`.
5112 To check out earlier revisions, you should use :hg:`update REV`.
5112 To cancel an uncommitted merge (and lose your changes),
5113 To cancel an uncommitted merge (and lose your changes),
5113 use :hg:`merge --abort`.
5114 use :hg:`merge --abort`.
5114
5115
5115 With no revision specified, revert the specified files or directories
5116 With no revision specified, revert the specified files or directories
5116 to the contents they had in the parent of the working directory.
5117 to the contents they had in the parent of the working directory.
5117 This restores the contents of files to an unmodified
5118 This restores the contents of files to an unmodified
5118 state and unschedules adds, removes, copies, and renames. If the
5119 state and unschedules adds, removes, copies, and renames. If the
5119 working directory has two parents, you must explicitly specify a
5120 working directory has two parents, you must explicitly specify a
5120 revision.
5121 revision.
5121
5122
5122 Using the -r/--rev or -d/--date options, revert the given files or
5123 Using the -r/--rev or -d/--date options, revert the given files or
5123 directories to their states as of a specific revision. Because
5124 directories to their states as of a specific revision. Because
5124 revert does not change the working directory parents, this will
5125 revert does not change the working directory parents, this will
5125 cause these files to appear modified. This can be helpful to "back
5126 cause these files to appear modified. This can be helpful to "back
5126 out" some or all of an earlier change. See :hg:`backout` for a
5127 out" some or all of an earlier change. See :hg:`backout` for a
5127 related method.
5128 related method.
5128
5129
5129 Modified files are saved with a .orig suffix before reverting.
5130 Modified files are saved with a .orig suffix before reverting.
5130 To disable these backups, use --no-backup. It is possible to store
5131 To disable these backups, use --no-backup. It is possible to store
5131 the backup files in a custom directory relative to the root of the
5132 the backup files in a custom directory relative to the root of the
5132 repository by setting the ``ui.origbackuppath`` configuration
5133 repository by setting the ``ui.origbackuppath`` configuration
5133 option.
5134 option.
5134
5135
5135 See :hg:`help dates` for a list of formats valid for -d/--date.
5136 See :hg:`help dates` for a list of formats valid for -d/--date.
5136
5137
5137 See :hg:`help backout` for a way to reverse the effect of an
5138 See :hg:`help backout` for a way to reverse the effect of an
5138 earlier changeset.
5139 earlier changeset.
5139
5140
5140 Returns 0 on success.
5141 Returns 0 on success.
5141 """
5142 """
5142
5143
5143 opts = pycompat.byteskwargs(opts)
5144 opts = pycompat.byteskwargs(opts)
5144 if opts.get("date"):
5145 if opts.get("date"):
5145 if opts.get("rev"):
5146 if opts.get("rev"):
5146 raise error.Abort(_("you can't specify a revision and a date"))
5147 raise error.Abort(_("you can't specify a revision and a date"))
5147 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5148 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5148
5149
5149 parent, p2 = repo.dirstate.parents()
5150 parent, p2 = repo.dirstate.parents()
5150 if not opts.get('rev') and p2 != nullid:
5151 if not opts.get('rev') and p2 != nullid:
5151 # revert after merge is a trap for new users (issue2915)
5152 # revert after merge is a trap for new users (issue2915)
5152 raise error.Abort(_('uncommitted merge with no revision specified'),
5153 raise error.Abort(_('uncommitted merge with no revision specified'),
5153 hint=_("use 'hg update' or see 'hg help revert'"))
5154 hint=_("use 'hg update' or see 'hg help revert'"))
5154
5155
5155 rev = opts.get('rev')
5156 rev = opts.get('rev')
5156 if rev:
5157 if rev:
5157 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5158 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5158 ctx = scmutil.revsingle(repo, rev)
5159 ctx = scmutil.revsingle(repo, rev)
5159
5160
5160 if (not (pats or opts.get('include') or opts.get('exclude') or
5161 if (not (pats or opts.get('include') or opts.get('exclude') or
5161 opts.get('all') or opts.get('interactive'))):
5162 opts.get('all') or opts.get('interactive'))):
5162 msg = _("no files or directories specified")
5163 msg = _("no files or directories specified")
5163 if p2 != nullid:
5164 if p2 != nullid:
5164 hint = _("uncommitted merge, use --all to discard all changes,"
5165 hint = _("uncommitted merge, use --all to discard all changes,"
5165 " or 'hg update -C .' to abort the merge")
5166 " or 'hg update -C .' to abort the merge")
5166 raise error.Abort(msg, hint=hint)
5167 raise error.Abort(msg, hint=hint)
5167 dirty = any(repo.status())
5168 dirty = any(repo.status())
5168 node = ctx.node()
5169 node = ctx.node()
5169 if node != parent:
5170 if node != parent:
5170 if dirty:
5171 if dirty:
5171 hint = _("uncommitted changes, use --all to discard all"
5172 hint = _("uncommitted changes, use --all to discard all"
5172 " changes, or 'hg update %d' to update") % ctx.rev()
5173 " changes, or 'hg update %d' to update") % ctx.rev()
5173 else:
5174 else:
5174 hint = _("use --all to revert all files,"
5175 hint = _("use --all to revert all files,"
5175 " or 'hg update %d' to update") % ctx.rev()
5176 " or 'hg update %d' to update") % ctx.rev()
5176 elif dirty:
5177 elif dirty:
5177 hint = _("uncommitted changes, use --all to discard all changes")
5178 hint = _("uncommitted changes, use --all to discard all changes")
5178 else:
5179 else:
5179 hint = _("use --all to revert all files")
5180 hint = _("use --all to revert all files")
5180 raise error.Abort(msg, hint=hint)
5181 raise error.Abort(msg, hint=hint)
5181
5182
5182 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5183 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5183 **pycompat.strkwargs(opts))
5184 **pycompat.strkwargs(opts))
5184
5185
5185 @command(
5186 @command(
5186 'rollback',
5187 'rollback',
5187 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5188 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5188 helpcategory=command.CATEGORY_MAINTENANCE)
5189 helpcategory=command.CATEGORY_MAINTENANCE)
5189 def rollback(ui, repo, **opts):
5190 def rollback(ui, repo, **opts):
5190 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5191 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5191
5192
5192 Please use :hg:`commit --amend` instead of rollback to correct
5193 Please use :hg:`commit --amend` instead of rollback to correct
5193 mistakes in the last commit.
5194 mistakes in the last commit.
5194
5195
5195 This command should be used with care. There is only one level of
5196 This command should be used with care. There is only one level of
5196 rollback, and there is no way to undo a rollback. It will also
5197 rollback, and there is no way to undo a rollback. It will also
5197 restore the dirstate at the time of the last transaction, losing
5198 restore the dirstate at the time of the last transaction, losing
5198 any dirstate changes since that time. This command does not alter
5199 any dirstate changes since that time. This command does not alter
5199 the working directory.
5200 the working directory.
5200
5201
5201 Transactions are used to encapsulate the effects of all commands
5202 Transactions are used to encapsulate the effects of all commands
5202 that create new changesets or propagate existing changesets into a
5203 that create new changesets or propagate existing changesets into a
5203 repository.
5204 repository.
5204
5205
5205 .. container:: verbose
5206 .. container:: verbose
5206
5207
5207 For example, the following commands are transactional, and their
5208 For example, the following commands are transactional, and their
5208 effects can be rolled back:
5209 effects can be rolled back:
5209
5210
5210 - commit
5211 - commit
5211 - import
5212 - import
5212 - pull
5213 - pull
5213 - push (with this repository as the destination)
5214 - push (with this repository as the destination)
5214 - unbundle
5215 - unbundle
5215
5216
5216 To avoid permanent data loss, rollback will refuse to rollback a
5217 To avoid permanent data loss, rollback will refuse to rollback a
5217 commit transaction if it isn't checked out. Use --force to
5218 commit transaction if it isn't checked out. Use --force to
5218 override this protection.
5219 override this protection.
5219
5220
5220 The rollback command can be entirely disabled by setting the
5221 The rollback command can be entirely disabled by setting the
5221 ``ui.rollback`` configuration setting to false. If you're here
5222 ``ui.rollback`` configuration setting to false. If you're here
5222 because you want to use rollback and it's disabled, you can
5223 because you want to use rollback and it's disabled, you can
5223 re-enable the command by setting ``ui.rollback`` to true.
5224 re-enable the command by setting ``ui.rollback`` to true.
5224
5225
5225 This command is not intended for use on public repositories. Once
5226 This command is not intended for use on public repositories. Once
5226 changes are visible for pull by other users, rolling a transaction
5227 changes are visible for pull by other users, rolling a transaction
5227 back locally is ineffective (someone else may already have pulled
5228 back locally is ineffective (someone else may already have pulled
5228 the changes). Furthermore, a race is possible with readers of the
5229 the changes). Furthermore, a race is possible with readers of the
5229 repository; for example an in-progress pull from the repository
5230 repository; for example an in-progress pull from the repository
5230 may fail if a rollback is performed.
5231 may fail if a rollback is performed.
5231
5232
5232 Returns 0 on success, 1 if no rollback data is available.
5233 Returns 0 on success, 1 if no rollback data is available.
5233 """
5234 """
5234 if not ui.configbool('ui', 'rollback'):
5235 if not ui.configbool('ui', 'rollback'):
5235 raise error.Abort(_('rollback is disabled because it is unsafe'),
5236 raise error.Abort(_('rollback is disabled because it is unsafe'),
5236 hint=('see `hg help -v rollback` for information'))
5237 hint=('see `hg help -v rollback` for information'))
5237 return repo.rollback(dryrun=opts.get(r'dry_run'),
5238 return repo.rollback(dryrun=opts.get(r'dry_run'),
5238 force=opts.get(r'force'))
5239 force=opts.get(r'force'))
5239
5240
5240 @command(
5241 @command(
5241 'root', [] + formatteropts, intents={INTENT_READONLY},
5242 'root', [] + formatteropts, intents={INTENT_READONLY},
5242 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5243 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5243 def root(ui, repo, **opts):
5244 def root(ui, repo, **opts):
5244 """print the root (top) of the current working directory
5245 """print the root (top) of the current working directory
5245
5246
5246 Print the root directory of the current repository.
5247 Print the root directory of the current repository.
5247
5248
5248 .. container:: verbose
5249 .. container:: verbose
5249
5250
5250 Template:
5251 Template:
5251
5252
5252 The following keywords are supported in addition to the common template
5253 The following keywords are supported in addition to the common template
5253 keywords and functions. See also :hg:`help templates`.
5254 keywords and functions. See also :hg:`help templates`.
5254
5255
5255 :hgpath: String. Path to the .hg directory.
5256 :hgpath: String. Path to the .hg directory.
5256 :storepath: String. Path to the directory holding versioned data.
5257 :storepath: String. Path to the directory holding versioned data.
5257
5258
5258 Returns 0 on success.
5259 Returns 0 on success.
5259 """
5260 """
5260 opts = pycompat.byteskwargs(opts)
5261 opts = pycompat.byteskwargs(opts)
5261 with ui.formatter('root', opts) as fm:
5262 with ui.formatter('root', opts) as fm:
5262 fm.startitem()
5263 fm.startitem()
5263 fm.write('reporoot', '%s\n', repo.root)
5264 fm.write('reporoot', '%s\n', repo.root)
5264 fm.data(hgpath=repo.path, storepath=repo.spath)
5265 fm.data(hgpath=repo.path, storepath=repo.spath)
5265
5266
5266 @command('serve',
5267 @command('serve',
5267 [('A', 'accesslog', '', _('name of access log file to write to'),
5268 [('A', 'accesslog', '', _('name of access log file to write to'),
5268 _('FILE')),
5269 _('FILE')),
5269 ('d', 'daemon', None, _('run server in background')),
5270 ('d', 'daemon', None, _('run server in background')),
5270 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5271 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5271 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5272 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5272 # use string type, then we can check if something was passed
5273 # use string type, then we can check if something was passed
5273 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5274 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5274 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5275 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5275 _('ADDR')),
5276 _('ADDR')),
5276 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5277 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5277 _('PREFIX')),
5278 _('PREFIX')),
5278 ('n', 'name', '',
5279 ('n', 'name', '',
5279 _('name to show in web pages (default: working directory)'), _('NAME')),
5280 _('name to show in web pages (default: working directory)'), _('NAME')),
5280 ('', 'web-conf', '',
5281 ('', 'web-conf', '',
5281 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5282 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5282 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5283 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5283 _('FILE')),
5284 _('FILE')),
5284 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5285 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5285 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5286 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5286 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5287 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5287 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5288 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5288 ('', 'style', '', _('template style to use'), _('STYLE')),
5289 ('', 'style', '', _('template style to use'), _('STYLE')),
5289 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5290 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5290 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5291 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5291 ('', 'print-url', None, _('start and print only the URL'))]
5292 ('', 'print-url', None, _('start and print only the URL'))]
5292 + subrepoopts,
5293 + subrepoopts,
5293 _('[OPTION]...'),
5294 _('[OPTION]...'),
5294 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5295 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5295 helpbasic=True, optionalrepo=True)
5296 helpbasic=True, optionalrepo=True)
5296 def serve(ui, repo, **opts):
5297 def serve(ui, repo, **opts):
5297 """start stand-alone webserver
5298 """start stand-alone webserver
5298
5299
5299 Start a local HTTP repository browser and pull server. You can use
5300 Start a local HTTP repository browser and pull server. You can use
5300 this for ad-hoc sharing and browsing of repositories. It is
5301 this for ad-hoc sharing and browsing of repositories. It is
5301 recommended to use a real web server to serve a repository for
5302 recommended to use a real web server to serve a repository for
5302 longer periods of time.
5303 longer periods of time.
5303
5304
5304 Please note that the server does not implement access control.
5305 Please note that the server does not implement access control.
5305 This means that, by default, anybody can read from the server and
5306 This means that, by default, anybody can read from the server and
5306 nobody can write to it by default. Set the ``web.allow-push``
5307 nobody can write to it by default. Set the ``web.allow-push``
5307 option to ``*`` to allow everybody to push to the server. You
5308 option to ``*`` to allow everybody to push to the server. You
5308 should use a real web server if you need to authenticate users.
5309 should use a real web server if you need to authenticate users.
5309
5310
5310 By default, the server logs accesses to stdout and errors to
5311 By default, the server logs accesses to stdout and errors to
5311 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5312 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5312 files.
5313 files.
5313
5314
5314 To have the server choose a free port number to listen on, specify
5315 To have the server choose a free port number to listen on, specify
5315 a port number of 0; in this case, the server will print the port
5316 a port number of 0; in this case, the server will print the port
5316 number it uses.
5317 number it uses.
5317
5318
5318 Returns 0 on success.
5319 Returns 0 on success.
5319 """
5320 """
5320
5321
5321 opts = pycompat.byteskwargs(opts)
5322 opts = pycompat.byteskwargs(opts)
5322 if opts["stdio"] and opts["cmdserver"]:
5323 if opts["stdio"] and opts["cmdserver"]:
5323 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5324 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5324 if opts["print_url"] and ui.verbose:
5325 if opts["print_url"] and ui.verbose:
5325 raise error.Abort(_("cannot use --print-url with --verbose"))
5326 raise error.Abort(_("cannot use --print-url with --verbose"))
5326
5327
5327 if opts["stdio"]:
5328 if opts["stdio"]:
5328 if repo is None:
5329 if repo is None:
5329 raise error.RepoError(_("there is no Mercurial repository here"
5330 raise error.RepoError(_("there is no Mercurial repository here"
5330 " (.hg not found)"))
5331 " (.hg not found)"))
5331 s = wireprotoserver.sshserver(ui, repo)
5332 s = wireprotoserver.sshserver(ui, repo)
5332 s.serve_forever()
5333 s.serve_forever()
5333
5334
5334 service = server.createservice(ui, repo, opts)
5335 service = server.createservice(ui, repo, opts)
5335 return server.runservice(opts, initfn=service.init, runfn=service.run)
5336 return server.runservice(opts, initfn=service.init, runfn=service.run)
5336
5337
5337 @command('shelve',
5338 @command('shelve',
5338 [('A', 'addremove', None,
5339 [('A', 'addremove', None,
5339 _('mark new/missing files as added/removed before shelving')),
5340 _('mark new/missing files as added/removed before shelving')),
5340 ('u', 'unknown', None,
5341 ('u', 'unknown', None,
5341 _('store unknown files in the shelve')),
5342 _('store unknown files in the shelve')),
5342 ('', 'cleanup', None,
5343 ('', 'cleanup', None,
5343 _('delete all shelved changes')),
5344 _('delete all shelved changes')),
5344 ('', 'date', '',
5345 ('', 'date', '',
5345 _('shelve with the specified commit date'), _('DATE')),
5346 _('shelve with the specified commit date'), _('DATE')),
5346 ('d', 'delete', None,
5347 ('d', 'delete', None,
5347 _('delete the named shelved change(s)')),
5348 _('delete the named shelved change(s)')),
5348 ('e', 'edit', False,
5349 ('e', 'edit', False,
5349 _('invoke editor on commit messages')),
5350 _('invoke editor on commit messages')),
5350 ('k', 'keep', False,
5351 ('k', 'keep', False,
5351 _('shelve, but keep changes in the working directory')),
5352 _('shelve, but keep changes in the working directory')),
5352 ('l', 'list', None,
5353 ('l', 'list', None,
5353 _('list current shelves')),
5354 _('list current shelves')),
5354 ('m', 'message', '',
5355 ('m', 'message', '',
5355 _('use text as shelve message'), _('TEXT')),
5356 _('use text as shelve message'), _('TEXT')),
5356 ('n', 'name', '',
5357 ('n', 'name', '',
5357 _('use the given name for the shelved commit'), _('NAME')),
5358 _('use the given name for the shelved commit'), _('NAME')),
5358 ('p', 'patch', None,
5359 ('p', 'patch', None,
5359 _('output patches for changes (provide the names of the shelved '
5360 _('output patches for changes (provide the names of the shelved '
5360 'changes as positional arguments)')),
5361 'changes as positional arguments)')),
5361 ('i', 'interactive', None,
5362 ('i', 'interactive', None,
5362 _('interactive mode')),
5363 _('interactive mode')),
5363 ('', 'stat', None,
5364 ('', 'stat', None,
5364 _('output diffstat-style summary of changes (provide the names of '
5365 _('output diffstat-style summary of changes (provide the names of '
5365 'the shelved changes as positional arguments)')
5366 'the shelved changes as positional arguments)')
5366 )] + cmdutil.walkopts,
5367 )] + cmdutil.walkopts,
5367 _('hg shelve [OPTION]... [FILE]...'),
5368 _('hg shelve [OPTION]... [FILE]...'),
5368 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5369 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5369 def shelve(ui, repo, *pats, **opts):
5370 def shelve(ui, repo, *pats, **opts):
5370 '''save and set aside changes from the working directory
5371 '''save and set aside changes from the working directory
5371
5372
5372 Shelving takes files that "hg status" reports as not clean, saves
5373 Shelving takes files that "hg status" reports as not clean, saves
5373 the modifications to a bundle (a shelved change), and reverts the
5374 the modifications to a bundle (a shelved change), and reverts the
5374 files so that their state in the working directory becomes clean.
5375 files so that their state in the working directory becomes clean.
5375
5376
5376 To restore these changes to the working directory, using "hg
5377 To restore these changes to the working directory, using "hg
5377 unshelve"; this will work even if you switch to a different
5378 unshelve"; this will work even if you switch to a different
5378 commit.
5379 commit.
5379
5380
5380 When no files are specified, "hg shelve" saves all not-clean
5381 When no files are specified, "hg shelve" saves all not-clean
5381 files. If specific files or directories are named, only changes to
5382 files. If specific files or directories are named, only changes to
5382 those files are shelved.
5383 those files are shelved.
5383
5384
5384 In bare shelve (when no files are specified, without interactive,
5385 In bare shelve (when no files are specified, without interactive,
5385 include and exclude option), shelving remembers information if the
5386 include and exclude option), shelving remembers information if the
5386 working directory was on newly created branch, in other words working
5387 working directory was on newly created branch, in other words working
5387 directory was on different branch than its first parent. In this
5388 directory was on different branch than its first parent. In this
5388 situation unshelving restores branch information to the working directory.
5389 situation unshelving restores branch information to the working directory.
5389
5390
5390 Each shelved change has a name that makes it easier to find later.
5391 Each shelved change has a name that makes it easier to find later.
5391 The name of a shelved change defaults to being based on the active
5392 The name of a shelved change defaults to being based on the active
5392 bookmark, or if there is no active bookmark, the current named
5393 bookmark, or if there is no active bookmark, the current named
5393 branch. To specify a different name, use ``--name``.
5394 branch. To specify a different name, use ``--name``.
5394
5395
5395 To see a list of existing shelved changes, use the ``--list``
5396 To see a list of existing shelved changes, use the ``--list``
5396 option. For each shelved change, this will print its name, age,
5397 option. For each shelved change, this will print its name, age,
5397 and description; use ``--patch`` or ``--stat`` for more details.
5398 and description; use ``--patch`` or ``--stat`` for more details.
5398
5399
5399 To delete specific shelved changes, use ``--delete``. To delete
5400 To delete specific shelved changes, use ``--delete``. To delete
5400 all shelved changes, use ``--cleanup``.
5401 all shelved changes, use ``--cleanup``.
5401 '''
5402 '''
5402 opts = pycompat.byteskwargs(opts)
5403 opts = pycompat.byteskwargs(opts)
5403 allowables = [
5404 allowables = [
5404 ('addremove', {'create'}), # 'create' is pseudo action
5405 ('addremove', {'create'}), # 'create' is pseudo action
5405 ('unknown', {'create'}),
5406 ('unknown', {'create'}),
5406 ('cleanup', {'cleanup'}),
5407 ('cleanup', {'cleanup'}),
5407 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
5408 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
5408 ('delete', {'delete'}),
5409 ('delete', {'delete'}),
5409 ('edit', {'create'}),
5410 ('edit', {'create'}),
5410 ('keep', {'create'}),
5411 ('keep', {'create'}),
5411 ('list', {'list'}),
5412 ('list', {'list'}),
5412 ('message', {'create'}),
5413 ('message', {'create'}),
5413 ('name', {'create'}),
5414 ('name', {'create'}),
5414 ('patch', {'patch', 'list'}),
5415 ('patch', {'patch', 'list'}),
5415 ('stat', {'stat', 'list'}),
5416 ('stat', {'stat', 'list'}),
5416 ]
5417 ]
5417 def checkopt(opt):
5418 def checkopt(opt):
5418 if opts.get(opt):
5419 if opts.get(opt):
5419 for i, allowable in allowables:
5420 for i, allowable in allowables:
5420 if opts[i] and opt not in allowable:
5421 if opts[i] and opt not in allowable:
5421 raise error.Abort(_("options '--%s' and '--%s' may not be "
5422 raise error.Abort(_("options '--%s' and '--%s' may not be "
5422 "used together") % (opt, i))
5423 "used together") % (opt, i))
5423 return True
5424 return True
5424 if checkopt('cleanup'):
5425 if checkopt('cleanup'):
5425 if pats:
5426 if pats:
5426 raise error.Abort(_("cannot specify names when using '--cleanup'"))
5427 raise error.Abort(_("cannot specify names when using '--cleanup'"))
5427 return shelvemod.cleanupcmd(ui, repo)
5428 return shelvemod.cleanupcmd(ui, repo)
5428 elif checkopt('delete'):
5429 elif checkopt('delete'):
5429 return shelvemod.deletecmd(ui, repo, pats)
5430 return shelvemod.deletecmd(ui, repo, pats)
5430 elif checkopt('list'):
5431 elif checkopt('list'):
5431 return shelvemod.listcmd(ui, repo, pats, opts)
5432 return shelvemod.listcmd(ui, repo, pats, opts)
5432 elif checkopt('patch') or checkopt('stat'):
5433 elif checkopt('patch') or checkopt('stat'):
5433 return shelvemod.patchcmds(ui, repo, pats, opts)
5434 return shelvemod.patchcmds(ui, repo, pats, opts)
5434 else:
5435 else:
5435 return shelvemod.createcmd(ui, repo, pats, opts)
5436 return shelvemod.createcmd(ui, repo, pats, opts)
5436
5437
5437 _NOTTERSE = 'nothing'
5438 _NOTTERSE = 'nothing'
5438
5439
5439 @command('status|st',
5440 @command('status|st',
5440 [('A', 'all', None, _('show status of all files')),
5441 [('A', 'all', None, _('show status of all files')),
5441 ('m', 'modified', None, _('show only modified files')),
5442 ('m', 'modified', None, _('show only modified files')),
5442 ('a', 'added', None, _('show only added files')),
5443 ('a', 'added', None, _('show only added files')),
5443 ('r', 'removed', None, _('show only removed files')),
5444 ('r', 'removed', None, _('show only removed files')),
5444 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5445 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5445 ('c', 'clean', None, _('show only files without changes')),
5446 ('c', 'clean', None, _('show only files without changes')),
5446 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5447 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5447 ('i', 'ignored', None, _('show only ignored files')),
5448 ('i', 'ignored', None, _('show only ignored files')),
5448 ('n', 'no-status', None, _('hide status prefix')),
5449 ('n', 'no-status', None, _('hide status prefix')),
5449 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5450 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5450 ('C', 'copies', None, _('show source of copied files')),
5451 ('C', 'copies', None, _('show source of copied files')),
5451 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5452 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5452 ('', 'rev', [], _('show difference from revision'), _('REV')),
5453 ('', 'rev', [], _('show difference from revision'), _('REV')),
5453 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5454 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5454 ] + walkopts + subrepoopts + formatteropts,
5455 ] + walkopts + subrepoopts + formatteropts,
5455 _('[OPTION]... [FILE]...'),
5456 _('[OPTION]... [FILE]...'),
5456 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5457 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5457 helpbasic=True, inferrepo=True,
5458 helpbasic=True, inferrepo=True,
5458 intents={INTENT_READONLY})
5459 intents={INTENT_READONLY})
5459 def status(ui, repo, *pats, **opts):
5460 def status(ui, repo, *pats, **opts):
5460 """show changed files in the working directory
5461 """show changed files in the working directory
5461
5462
5462 Show status of files in the repository. If names are given, only
5463 Show status of files in the repository. If names are given, only
5463 files that match are shown. Files that are clean or ignored or
5464 files that match are shown. Files that are clean or ignored or
5464 the source of a copy/move operation, are not listed unless
5465 the source of a copy/move operation, are not listed unless
5465 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5466 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5466 Unless options described with "show only ..." are given, the
5467 Unless options described with "show only ..." are given, the
5467 options -mardu are used.
5468 options -mardu are used.
5468
5469
5469 Option -q/--quiet hides untracked (unknown and ignored) files
5470 Option -q/--quiet hides untracked (unknown and ignored) files
5470 unless explicitly requested with -u/--unknown or -i/--ignored.
5471 unless explicitly requested with -u/--unknown or -i/--ignored.
5471
5472
5472 .. note::
5473 .. note::
5473
5474
5474 :hg:`status` may appear to disagree with diff if permissions have
5475 :hg:`status` may appear to disagree with diff if permissions have
5475 changed or a merge has occurred. The standard diff format does
5476 changed or a merge has occurred. The standard diff format does
5476 not report permission changes and diff only reports changes
5477 not report permission changes and diff only reports changes
5477 relative to one merge parent.
5478 relative to one merge parent.
5478
5479
5479 If one revision is given, it is used as the base revision.
5480 If one revision is given, it is used as the base revision.
5480 If two revisions are given, the differences between them are
5481 If two revisions are given, the differences between them are
5481 shown. The --change option can also be used as a shortcut to list
5482 shown. The --change option can also be used as a shortcut to list
5482 the changed files of a revision from its first parent.
5483 the changed files of a revision from its first parent.
5483
5484
5484 The codes used to show the status of files are::
5485 The codes used to show the status of files are::
5485
5486
5486 M = modified
5487 M = modified
5487 A = added
5488 A = added
5488 R = removed
5489 R = removed
5489 C = clean
5490 C = clean
5490 ! = missing (deleted by non-hg command, but still tracked)
5491 ! = missing (deleted by non-hg command, but still tracked)
5491 ? = not tracked
5492 ? = not tracked
5492 I = ignored
5493 I = ignored
5493 = origin of the previous file (with --copies)
5494 = origin of the previous file (with --copies)
5494
5495
5495 .. container:: verbose
5496 .. container:: verbose
5496
5497
5497 The -t/--terse option abbreviates the output by showing only the directory
5498 The -t/--terse option abbreviates the output by showing only the directory
5498 name if all the files in it share the same status. The option takes an
5499 name if all the files in it share the same status. The option takes an
5499 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5500 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5500 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5501 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5501 for 'ignored' and 'c' for clean.
5502 for 'ignored' and 'c' for clean.
5502
5503
5503 It abbreviates only those statuses which are passed. Note that clean and
5504 It abbreviates only those statuses which are passed. Note that clean and
5504 ignored files are not displayed with '--terse ic' unless the -c/--clean
5505 ignored files are not displayed with '--terse ic' unless the -c/--clean
5505 and -i/--ignored options are also used.
5506 and -i/--ignored options are also used.
5506
5507
5507 The -v/--verbose option shows information when the repository is in an
5508 The -v/--verbose option shows information when the repository is in an
5508 unfinished merge, shelve, rebase state etc. You can have this behavior
5509 unfinished merge, shelve, rebase state etc. You can have this behavior
5509 turned on by default by enabling the ``commands.status.verbose`` option.
5510 turned on by default by enabling the ``commands.status.verbose`` option.
5510
5511
5511 You can skip displaying some of these states by setting
5512 You can skip displaying some of these states by setting
5512 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5513 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5513 'histedit', 'merge', 'rebase', or 'unshelve'.
5514 'histedit', 'merge', 'rebase', or 'unshelve'.
5514
5515
5515 Template:
5516 Template:
5516
5517
5517 The following keywords are supported in addition to the common template
5518 The following keywords are supported in addition to the common template
5518 keywords and functions. See also :hg:`help templates`.
5519 keywords and functions. See also :hg:`help templates`.
5519
5520
5520 :path: String. Repository-absolute path of the file.
5521 :path: String. Repository-absolute path of the file.
5521 :source: String. Repository-absolute path of the file originated from.
5522 :source: String. Repository-absolute path of the file originated from.
5522 Available if ``--copies`` is specified.
5523 Available if ``--copies`` is specified.
5523 :status: String. Character denoting file's status.
5524 :status: String. Character denoting file's status.
5524
5525
5525 Examples:
5526 Examples:
5526
5527
5527 - show changes in the working directory relative to a
5528 - show changes in the working directory relative to a
5528 changeset::
5529 changeset::
5529
5530
5530 hg status --rev 9353
5531 hg status --rev 9353
5531
5532
5532 - show changes in the working directory relative to the
5533 - show changes in the working directory relative to the
5533 current directory (see :hg:`help patterns` for more information)::
5534 current directory (see :hg:`help patterns` for more information)::
5534
5535
5535 hg status re:
5536 hg status re:
5536
5537
5537 - show all changes including copies in an existing changeset::
5538 - show all changes including copies in an existing changeset::
5538
5539
5539 hg status --copies --change 9353
5540 hg status --copies --change 9353
5540
5541
5541 - get a NUL separated list of added files, suitable for xargs::
5542 - get a NUL separated list of added files, suitable for xargs::
5542
5543
5543 hg status -an0
5544 hg status -an0
5544
5545
5545 - show more information about the repository status, abbreviating
5546 - show more information about the repository status, abbreviating
5546 added, removed, modified, deleted, and untracked paths::
5547 added, removed, modified, deleted, and untracked paths::
5547
5548
5548 hg status -v -t mardu
5549 hg status -v -t mardu
5549
5550
5550 Returns 0 on success.
5551 Returns 0 on success.
5551
5552
5552 """
5553 """
5553
5554
5554 opts = pycompat.byteskwargs(opts)
5555 opts = pycompat.byteskwargs(opts)
5555 revs = opts.get('rev')
5556 revs = opts.get('rev')
5556 change = opts.get('change')
5557 change = opts.get('change')
5557 terse = opts.get('terse')
5558 terse = opts.get('terse')
5558 if terse is _NOTTERSE:
5559 if terse is _NOTTERSE:
5559 if revs:
5560 if revs:
5560 terse = ''
5561 terse = ''
5561 else:
5562 else:
5562 terse = ui.config('commands', 'status.terse')
5563 terse = ui.config('commands', 'status.terse')
5563
5564
5564 if revs and change:
5565 if revs and change:
5565 msg = _('cannot specify --rev and --change at the same time')
5566 msg = _('cannot specify --rev and --change at the same time')
5566 raise error.Abort(msg)
5567 raise error.Abort(msg)
5567 elif revs and terse:
5568 elif revs and terse:
5568 msg = _('cannot use --terse with --rev')
5569 msg = _('cannot use --terse with --rev')
5569 raise error.Abort(msg)
5570 raise error.Abort(msg)
5570 elif change:
5571 elif change:
5571 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5572 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5572 ctx2 = scmutil.revsingle(repo, change, None)
5573 ctx2 = scmutil.revsingle(repo, change, None)
5573 ctx1 = ctx2.p1()
5574 ctx1 = ctx2.p1()
5574 else:
5575 else:
5575 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5576 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5576 ctx1, ctx2 = scmutil.revpair(repo, revs)
5577 ctx1, ctx2 = scmutil.revpair(repo, revs)
5577
5578
5578 forcerelativevalue = None
5579 forcerelativevalue = None
5579 if ui.hasconfig('commands', 'status.relative'):
5580 if ui.hasconfig('commands', 'status.relative'):
5580 forcerelativevalue = ui.configbool('commands', 'status.relative')
5581 forcerelativevalue = ui.configbool('commands', 'status.relative')
5581 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5582 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5582 forcerelativevalue=forcerelativevalue)
5583 forcerelativevalue=forcerelativevalue)
5583
5584
5584 if opts.get('print0'):
5585 if opts.get('print0'):
5585 end = '\0'
5586 end = '\0'
5586 else:
5587 else:
5587 end = '\n'
5588 end = '\n'
5588 copy = {}
5589 copy = {}
5589 states = 'modified added removed deleted unknown ignored clean'.split()
5590 states = 'modified added removed deleted unknown ignored clean'.split()
5590 show = [k for k in states if opts.get(k)]
5591 show = [k for k in states if opts.get(k)]
5591 if opts.get('all'):
5592 if opts.get('all'):
5592 show += ui.quiet and (states[:4] + ['clean']) or states
5593 show += ui.quiet and (states[:4] + ['clean']) or states
5593
5594
5594 if not show:
5595 if not show:
5595 if ui.quiet:
5596 if ui.quiet:
5596 show = states[:4]
5597 show = states[:4]
5597 else:
5598 else:
5598 show = states[:5]
5599 show = states[:5]
5599
5600
5600 m = scmutil.match(ctx2, pats, opts)
5601 m = scmutil.match(ctx2, pats, opts)
5601 if terse:
5602 if terse:
5602 # we need to compute clean and unknown to terse
5603 # we need to compute clean and unknown to terse
5603 stat = repo.status(ctx1.node(), ctx2.node(), m,
5604 stat = repo.status(ctx1.node(), ctx2.node(), m,
5604 'ignored' in show or 'i' in terse,
5605 'ignored' in show or 'i' in terse,
5605 clean=True, unknown=True,
5606 clean=True, unknown=True,
5606 listsubrepos=opts.get('subrepos'))
5607 listsubrepos=opts.get('subrepos'))
5607
5608
5608 stat = cmdutil.tersedir(stat, terse)
5609 stat = cmdutil.tersedir(stat, terse)
5609 else:
5610 else:
5610 stat = repo.status(ctx1.node(), ctx2.node(), m,
5611 stat = repo.status(ctx1.node(), ctx2.node(), m,
5611 'ignored' in show, 'clean' in show,
5612 'ignored' in show, 'clean' in show,
5612 'unknown' in show, opts.get('subrepos'))
5613 'unknown' in show, opts.get('subrepos'))
5613
5614
5614 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5615 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5615
5616
5616 if (opts.get('all') or opts.get('copies')
5617 if (opts.get('all') or opts.get('copies')
5617 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5618 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5618 copy = copies.pathcopies(ctx1, ctx2, m)
5619 copy = copies.pathcopies(ctx1, ctx2, m)
5619
5620
5620 ui.pager('status')
5621 ui.pager('status')
5621 fm = ui.formatter('status', opts)
5622 fm = ui.formatter('status', opts)
5622 fmt = '%s' + end
5623 fmt = '%s' + end
5623 showchar = not opts.get('no_status')
5624 showchar = not opts.get('no_status')
5624
5625
5625 for state, char, files in changestates:
5626 for state, char, files in changestates:
5626 if state in show:
5627 if state in show:
5627 label = 'status.' + state
5628 label = 'status.' + state
5628 for f in files:
5629 for f in files:
5629 fm.startitem()
5630 fm.startitem()
5630 fm.context(ctx=ctx2)
5631 fm.context(ctx=ctx2)
5631 fm.data(path=f)
5632 fm.data(path=f)
5632 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5633 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5633 fm.plain(fmt % uipathfn(f), label=label)
5634 fm.plain(fmt % uipathfn(f), label=label)
5634 if f in copy:
5635 if f in copy:
5635 fm.data(source=copy[f])
5636 fm.data(source=copy[f])
5636 fm.plain((' %s' + end) % uipathfn(copy[f]),
5637 fm.plain((' %s' + end) % uipathfn(copy[f]),
5637 label='status.copied')
5638 label='status.copied')
5638
5639
5639 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5640 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5640 and not ui.plain()):
5641 and not ui.plain()):
5641 cmdutil.morestatus(repo, fm)
5642 cmdutil.morestatus(repo, fm)
5642 fm.end()
5643 fm.end()
5643
5644
5644 @command('summary|sum',
5645 @command('summary|sum',
5645 [('', 'remote', None, _('check for push and pull'))],
5646 [('', 'remote', None, _('check for push and pull'))],
5646 '[--remote]',
5647 '[--remote]',
5647 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5648 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5648 helpbasic=True,
5649 helpbasic=True,
5649 intents={INTENT_READONLY})
5650 intents={INTENT_READONLY})
5650 def summary(ui, repo, **opts):
5651 def summary(ui, repo, **opts):
5651 """summarize working directory state
5652 """summarize working directory state
5652
5653
5653 This generates a brief summary of the working directory state,
5654 This generates a brief summary of the working directory state,
5654 including parents, branch, commit status, phase and available updates.
5655 including parents, branch, commit status, phase and available updates.
5655
5656
5656 With the --remote option, this will check the default paths for
5657 With the --remote option, this will check the default paths for
5657 incoming and outgoing changes. This can be time-consuming.
5658 incoming and outgoing changes. This can be time-consuming.
5658
5659
5659 Returns 0 on success.
5660 Returns 0 on success.
5660 """
5661 """
5661
5662
5662 opts = pycompat.byteskwargs(opts)
5663 opts = pycompat.byteskwargs(opts)
5663 ui.pager('summary')
5664 ui.pager('summary')
5664 ctx = repo[None]
5665 ctx = repo[None]
5665 parents = ctx.parents()
5666 parents = ctx.parents()
5666 pnode = parents[0].node()
5667 pnode = parents[0].node()
5667 marks = []
5668 marks = []
5668
5669
5669 try:
5670 try:
5670 ms = mergemod.mergestate.read(repo)
5671 ms = mergemod.mergestate.read(repo)
5671 except error.UnsupportedMergeRecords as e:
5672 except error.UnsupportedMergeRecords as e:
5672 s = ' '.join(e.recordtypes)
5673 s = ' '.join(e.recordtypes)
5673 ui.warn(
5674 ui.warn(
5674 _('warning: merge state has unsupported record types: %s\n') % s)
5675 _('warning: merge state has unsupported record types: %s\n') % s)
5675 unresolved = []
5676 unresolved = []
5676 else:
5677 else:
5677 unresolved = list(ms.unresolved())
5678 unresolved = list(ms.unresolved())
5678
5679
5679 for p in parents:
5680 for p in parents:
5680 # label with log.changeset (instead of log.parent) since this
5681 # label with log.changeset (instead of log.parent) since this
5681 # shows a working directory parent *changeset*:
5682 # shows a working directory parent *changeset*:
5682 # i18n: column positioning for "hg summary"
5683 # i18n: column positioning for "hg summary"
5683 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5684 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5684 label=logcmdutil.changesetlabels(p))
5685 label=logcmdutil.changesetlabels(p))
5685 ui.write(' '.join(p.tags()), label='log.tag')
5686 ui.write(' '.join(p.tags()), label='log.tag')
5686 if p.bookmarks():
5687 if p.bookmarks():
5687 marks.extend(p.bookmarks())
5688 marks.extend(p.bookmarks())
5688 if p.rev() == -1:
5689 if p.rev() == -1:
5689 if not len(repo):
5690 if not len(repo):
5690 ui.write(_(' (empty repository)'))
5691 ui.write(_(' (empty repository)'))
5691 else:
5692 else:
5692 ui.write(_(' (no revision checked out)'))
5693 ui.write(_(' (no revision checked out)'))
5693 if p.obsolete():
5694 if p.obsolete():
5694 ui.write(_(' (obsolete)'))
5695 ui.write(_(' (obsolete)'))
5695 if p.isunstable():
5696 if p.isunstable():
5696 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5697 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5697 for instability in p.instabilities())
5698 for instability in p.instabilities())
5698 ui.write(' ('
5699 ui.write(' ('
5699 + ', '.join(instabilities)
5700 + ', '.join(instabilities)
5700 + ')')
5701 + ')')
5701 ui.write('\n')
5702 ui.write('\n')
5702 if p.description():
5703 if p.description():
5703 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5704 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5704 label='log.summary')
5705 label='log.summary')
5705
5706
5706 branch = ctx.branch()
5707 branch = ctx.branch()
5707 bheads = repo.branchheads(branch)
5708 bheads = repo.branchheads(branch)
5708 # i18n: column positioning for "hg summary"
5709 # i18n: column positioning for "hg summary"
5709 m = _('branch: %s\n') % branch
5710 m = _('branch: %s\n') % branch
5710 if branch != 'default':
5711 if branch != 'default':
5711 ui.write(m, label='log.branch')
5712 ui.write(m, label='log.branch')
5712 else:
5713 else:
5713 ui.status(m, label='log.branch')
5714 ui.status(m, label='log.branch')
5714
5715
5715 if marks:
5716 if marks:
5716 active = repo._activebookmark
5717 active = repo._activebookmark
5717 # i18n: column positioning for "hg summary"
5718 # i18n: column positioning for "hg summary"
5718 ui.write(_('bookmarks:'), label='log.bookmark')
5719 ui.write(_('bookmarks:'), label='log.bookmark')
5719 if active is not None:
5720 if active is not None:
5720 if active in marks:
5721 if active in marks:
5721 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5722 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5722 marks.remove(active)
5723 marks.remove(active)
5723 else:
5724 else:
5724 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5725 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5725 for m in marks:
5726 for m in marks:
5726 ui.write(' ' + m, label='log.bookmark')
5727 ui.write(' ' + m, label='log.bookmark')
5727 ui.write('\n', label='log.bookmark')
5728 ui.write('\n', label='log.bookmark')
5728
5729
5729 status = repo.status(unknown=True)
5730 status = repo.status(unknown=True)
5730
5731
5731 c = repo.dirstate.copies()
5732 c = repo.dirstate.copies()
5732 copied, renamed = [], []
5733 copied, renamed = [], []
5733 for d, s in c.iteritems():
5734 for d, s in c.iteritems():
5734 if s in status.removed:
5735 if s in status.removed:
5735 status.removed.remove(s)
5736 status.removed.remove(s)
5736 renamed.append(d)
5737 renamed.append(d)
5737 else:
5738 else:
5738 copied.append(d)
5739 copied.append(d)
5739 if d in status.added:
5740 if d in status.added:
5740 status.added.remove(d)
5741 status.added.remove(d)
5741
5742
5742 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5743 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5743
5744
5744 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5745 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5745 (ui.label(_('%d added'), 'status.added'), status.added),
5746 (ui.label(_('%d added'), 'status.added'), status.added),
5746 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5747 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5747 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5748 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5748 (ui.label(_('%d copied'), 'status.copied'), copied),
5749 (ui.label(_('%d copied'), 'status.copied'), copied),
5749 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5750 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5750 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5751 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5751 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5752 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5752 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5753 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5753 t = []
5754 t = []
5754 for l, s in labels:
5755 for l, s in labels:
5755 if s:
5756 if s:
5756 t.append(l % len(s))
5757 t.append(l % len(s))
5757
5758
5758 t = ', '.join(t)
5759 t = ', '.join(t)
5759 cleanworkdir = False
5760 cleanworkdir = False
5760
5761
5761 if repo.vfs.exists('graftstate'):
5762 if repo.vfs.exists('graftstate'):
5762 t += _(' (graft in progress)')
5763 t += _(' (graft in progress)')
5763 if repo.vfs.exists('updatestate'):
5764 if repo.vfs.exists('updatestate'):
5764 t += _(' (interrupted update)')
5765 t += _(' (interrupted update)')
5765 elif len(parents) > 1:
5766 elif len(parents) > 1:
5766 t += _(' (merge)')
5767 t += _(' (merge)')
5767 elif branch != parents[0].branch():
5768 elif branch != parents[0].branch():
5768 t += _(' (new branch)')
5769 t += _(' (new branch)')
5769 elif (parents[0].closesbranch() and
5770 elif (parents[0].closesbranch() and
5770 pnode in repo.branchheads(branch, closed=True)):
5771 pnode in repo.branchheads(branch, closed=True)):
5771 t += _(' (head closed)')
5772 t += _(' (head closed)')
5772 elif not (status.modified or status.added or status.removed or renamed or
5773 elif not (status.modified or status.added or status.removed or renamed or
5773 copied or subs):
5774 copied or subs):
5774 t += _(' (clean)')
5775 t += _(' (clean)')
5775 cleanworkdir = True
5776 cleanworkdir = True
5776 elif pnode not in bheads:
5777 elif pnode not in bheads:
5777 t += _(' (new branch head)')
5778 t += _(' (new branch head)')
5778
5779
5779 if parents:
5780 if parents:
5780 pendingphase = max(p.phase() for p in parents)
5781 pendingphase = max(p.phase() for p in parents)
5781 else:
5782 else:
5782 pendingphase = phases.public
5783 pendingphase = phases.public
5783
5784
5784 if pendingphase > phases.newcommitphase(ui):
5785 if pendingphase > phases.newcommitphase(ui):
5785 t += ' (%s)' % phases.phasenames[pendingphase]
5786 t += ' (%s)' % phases.phasenames[pendingphase]
5786
5787
5787 if cleanworkdir:
5788 if cleanworkdir:
5788 # i18n: column positioning for "hg summary"
5789 # i18n: column positioning for "hg summary"
5789 ui.status(_('commit: %s\n') % t.strip())
5790 ui.status(_('commit: %s\n') % t.strip())
5790 else:
5791 else:
5791 # i18n: column positioning for "hg summary"
5792 # i18n: column positioning for "hg summary"
5792 ui.write(_('commit: %s\n') % t.strip())
5793 ui.write(_('commit: %s\n') % t.strip())
5793
5794
5794 # all ancestors of branch heads - all ancestors of parent = new csets
5795 # all ancestors of branch heads - all ancestors of parent = new csets
5795 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5796 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5796 bheads))
5797 bheads))
5797
5798
5798 if new == 0:
5799 if new == 0:
5799 # i18n: column positioning for "hg summary"
5800 # i18n: column positioning for "hg summary"
5800 ui.status(_('update: (current)\n'))
5801 ui.status(_('update: (current)\n'))
5801 elif pnode not in bheads:
5802 elif pnode not in bheads:
5802 # i18n: column positioning for "hg summary"
5803 # i18n: column positioning for "hg summary"
5803 ui.write(_('update: %d new changesets (update)\n') % new)
5804 ui.write(_('update: %d new changesets (update)\n') % new)
5804 else:
5805 else:
5805 # i18n: column positioning for "hg summary"
5806 # i18n: column positioning for "hg summary"
5806 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5807 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5807 (new, len(bheads)))
5808 (new, len(bheads)))
5808
5809
5809 t = []
5810 t = []
5810 draft = len(repo.revs('draft()'))
5811 draft = len(repo.revs('draft()'))
5811 if draft:
5812 if draft:
5812 t.append(_('%d draft') % draft)
5813 t.append(_('%d draft') % draft)
5813 secret = len(repo.revs('secret()'))
5814 secret = len(repo.revs('secret()'))
5814 if secret:
5815 if secret:
5815 t.append(_('%d secret') % secret)
5816 t.append(_('%d secret') % secret)
5816
5817
5817 if draft or secret:
5818 if draft or secret:
5818 ui.status(_('phases: %s\n') % ', '.join(t))
5819 ui.status(_('phases: %s\n') % ', '.join(t))
5819
5820
5820 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5821 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5821 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5822 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5822 numtrouble = len(repo.revs(trouble + "()"))
5823 numtrouble = len(repo.revs(trouble + "()"))
5823 # We write all the possibilities to ease translation
5824 # We write all the possibilities to ease translation
5824 troublemsg = {
5825 troublemsg = {
5825 "orphan": _("orphan: %d changesets"),
5826 "orphan": _("orphan: %d changesets"),
5826 "contentdivergent": _("content-divergent: %d changesets"),
5827 "contentdivergent": _("content-divergent: %d changesets"),
5827 "phasedivergent": _("phase-divergent: %d changesets"),
5828 "phasedivergent": _("phase-divergent: %d changesets"),
5828 }
5829 }
5829 if numtrouble > 0:
5830 if numtrouble > 0:
5830 ui.status(troublemsg[trouble] % numtrouble + "\n")
5831 ui.status(troublemsg[trouble] % numtrouble + "\n")
5831
5832
5832 cmdutil.summaryhooks(ui, repo)
5833 cmdutil.summaryhooks(ui, repo)
5833
5834
5834 if opts.get('remote'):
5835 if opts.get('remote'):
5835 needsincoming, needsoutgoing = True, True
5836 needsincoming, needsoutgoing = True, True
5836 else:
5837 else:
5837 needsincoming, needsoutgoing = False, False
5838 needsincoming, needsoutgoing = False, False
5838 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5839 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5839 if i:
5840 if i:
5840 needsincoming = True
5841 needsincoming = True
5841 if o:
5842 if o:
5842 needsoutgoing = True
5843 needsoutgoing = True
5843 if not needsincoming and not needsoutgoing:
5844 if not needsincoming and not needsoutgoing:
5844 return
5845 return
5845
5846
5846 def getincoming():
5847 def getincoming():
5847 source, branches = hg.parseurl(ui.expandpath('default'))
5848 source, branches = hg.parseurl(ui.expandpath('default'))
5848 sbranch = branches[0]
5849 sbranch = branches[0]
5849 try:
5850 try:
5850 other = hg.peer(repo, {}, source)
5851 other = hg.peer(repo, {}, source)
5851 except error.RepoError:
5852 except error.RepoError:
5852 if opts.get('remote'):
5853 if opts.get('remote'):
5853 raise
5854 raise
5854 return source, sbranch, None, None, None
5855 return source, sbranch, None, None, None
5855 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5856 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5856 if revs:
5857 if revs:
5857 revs = [other.lookup(rev) for rev in revs]
5858 revs = [other.lookup(rev) for rev in revs]
5858 ui.debug('comparing with %s\n' % util.hidepassword(source))
5859 ui.debug('comparing with %s\n' % util.hidepassword(source))
5859 repo.ui.pushbuffer()
5860 repo.ui.pushbuffer()
5860 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5861 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5861 repo.ui.popbuffer()
5862 repo.ui.popbuffer()
5862 return source, sbranch, other, commoninc, commoninc[1]
5863 return source, sbranch, other, commoninc, commoninc[1]
5863
5864
5864 if needsincoming:
5865 if needsincoming:
5865 source, sbranch, sother, commoninc, incoming = getincoming()
5866 source, sbranch, sother, commoninc, incoming = getincoming()
5866 else:
5867 else:
5867 source = sbranch = sother = commoninc = incoming = None
5868 source = sbranch = sother = commoninc = incoming = None
5868
5869
5869 def getoutgoing():
5870 def getoutgoing():
5870 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5871 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5871 dbranch = branches[0]
5872 dbranch = branches[0]
5872 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5873 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5873 if source != dest:
5874 if source != dest:
5874 try:
5875 try:
5875 dother = hg.peer(repo, {}, dest)
5876 dother = hg.peer(repo, {}, dest)
5876 except error.RepoError:
5877 except error.RepoError:
5877 if opts.get('remote'):
5878 if opts.get('remote'):
5878 raise
5879 raise
5879 return dest, dbranch, None, None
5880 return dest, dbranch, None, None
5880 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5881 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5881 elif sother is None:
5882 elif sother is None:
5882 # there is no explicit destination peer, but source one is invalid
5883 # there is no explicit destination peer, but source one is invalid
5883 return dest, dbranch, None, None
5884 return dest, dbranch, None, None
5884 else:
5885 else:
5885 dother = sother
5886 dother = sother
5886 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5887 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5887 common = None
5888 common = None
5888 else:
5889 else:
5889 common = commoninc
5890 common = commoninc
5890 if revs:
5891 if revs:
5891 revs = [repo.lookup(rev) for rev in revs]
5892 revs = [repo.lookup(rev) for rev in revs]
5892 repo.ui.pushbuffer()
5893 repo.ui.pushbuffer()
5893 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5894 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5894 commoninc=common)
5895 commoninc=common)
5895 repo.ui.popbuffer()
5896 repo.ui.popbuffer()
5896 return dest, dbranch, dother, outgoing
5897 return dest, dbranch, dother, outgoing
5897
5898
5898 if needsoutgoing:
5899 if needsoutgoing:
5899 dest, dbranch, dother, outgoing = getoutgoing()
5900 dest, dbranch, dother, outgoing = getoutgoing()
5900 else:
5901 else:
5901 dest = dbranch = dother = outgoing = None
5902 dest = dbranch = dother = outgoing = None
5902
5903
5903 if opts.get('remote'):
5904 if opts.get('remote'):
5904 t = []
5905 t = []
5905 if incoming:
5906 if incoming:
5906 t.append(_('1 or more incoming'))
5907 t.append(_('1 or more incoming'))
5907 o = outgoing.missing
5908 o = outgoing.missing
5908 if o:
5909 if o:
5909 t.append(_('%d outgoing') % len(o))
5910 t.append(_('%d outgoing') % len(o))
5910 other = dother or sother
5911 other = dother or sother
5911 if 'bookmarks' in other.listkeys('namespaces'):
5912 if 'bookmarks' in other.listkeys('namespaces'):
5912 counts = bookmarks.summary(repo, other)
5913 counts = bookmarks.summary(repo, other)
5913 if counts[0] > 0:
5914 if counts[0] > 0:
5914 t.append(_('%d incoming bookmarks') % counts[0])
5915 t.append(_('%d incoming bookmarks') % counts[0])
5915 if counts[1] > 0:
5916 if counts[1] > 0:
5916 t.append(_('%d outgoing bookmarks') % counts[1])
5917 t.append(_('%d outgoing bookmarks') % counts[1])
5917
5918
5918 if t:
5919 if t:
5919 # i18n: column positioning for "hg summary"
5920 # i18n: column positioning for "hg summary"
5920 ui.write(_('remote: %s\n') % (', '.join(t)))
5921 ui.write(_('remote: %s\n') % (', '.join(t)))
5921 else:
5922 else:
5922 # i18n: column positioning for "hg summary"
5923 # i18n: column positioning for "hg summary"
5923 ui.status(_('remote: (synced)\n'))
5924 ui.status(_('remote: (synced)\n'))
5924
5925
5925 cmdutil.summaryremotehooks(ui, repo, opts,
5926 cmdutil.summaryremotehooks(ui, repo, opts,
5926 ((source, sbranch, sother, commoninc),
5927 ((source, sbranch, sother, commoninc),
5927 (dest, dbranch, dother, outgoing)))
5928 (dest, dbranch, dother, outgoing)))
5928
5929
5929 @command('tag',
5930 @command('tag',
5930 [('f', 'force', None, _('force tag')),
5931 [('f', 'force', None, _('force tag')),
5931 ('l', 'local', None, _('make the tag local')),
5932 ('l', 'local', None, _('make the tag local')),
5932 ('r', 'rev', '', _('revision to tag'), _('REV')),
5933 ('r', 'rev', '', _('revision to tag'), _('REV')),
5933 ('', 'remove', None, _('remove a tag')),
5934 ('', 'remove', None, _('remove a tag')),
5934 # -l/--local is already there, commitopts cannot be used
5935 # -l/--local is already there, commitopts cannot be used
5935 ('e', 'edit', None, _('invoke editor on commit messages')),
5936 ('e', 'edit', None, _('invoke editor on commit messages')),
5936 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5937 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5937 ] + commitopts2,
5938 ] + commitopts2,
5938 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5939 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5939 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5940 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5940 def tag(ui, repo, name1, *names, **opts):
5941 def tag(ui, repo, name1, *names, **opts):
5941 """add one or more tags for the current or given revision
5942 """add one or more tags for the current or given revision
5942
5943
5943 Name a particular revision using <name>.
5944 Name a particular revision using <name>.
5944
5945
5945 Tags are used to name particular revisions of the repository and are
5946 Tags are used to name particular revisions of the repository and are
5946 very useful to compare different revisions, to go back to significant
5947 very useful to compare different revisions, to go back to significant
5947 earlier versions or to mark branch points as releases, etc. Changing
5948 earlier versions or to mark branch points as releases, etc. Changing
5948 an existing tag is normally disallowed; use -f/--force to override.
5949 an existing tag is normally disallowed; use -f/--force to override.
5949
5950
5950 If no revision is given, the parent of the working directory is
5951 If no revision is given, the parent of the working directory is
5951 used.
5952 used.
5952
5953
5953 To facilitate version control, distribution, and merging of tags,
5954 To facilitate version control, distribution, and merging of tags,
5954 they are stored as a file named ".hgtags" which is managed similarly
5955 they are stored as a file named ".hgtags" which is managed similarly
5955 to other project files and can be hand-edited if necessary. This
5956 to other project files and can be hand-edited if necessary. This
5956 also means that tagging creates a new commit. The file
5957 also means that tagging creates a new commit. The file
5957 ".hg/localtags" is used for local tags (not shared among
5958 ".hg/localtags" is used for local tags (not shared among
5958 repositories).
5959 repositories).
5959
5960
5960 Tag commits are usually made at the head of a branch. If the parent
5961 Tag commits are usually made at the head of a branch. If the parent
5961 of the working directory is not a branch head, :hg:`tag` aborts; use
5962 of the working directory is not a branch head, :hg:`tag` aborts; use
5962 -f/--force to force the tag commit to be based on a non-head
5963 -f/--force to force the tag commit to be based on a non-head
5963 changeset.
5964 changeset.
5964
5965
5965 See :hg:`help dates` for a list of formats valid for -d/--date.
5966 See :hg:`help dates` for a list of formats valid for -d/--date.
5966
5967
5967 Since tag names have priority over branch names during revision
5968 Since tag names have priority over branch names during revision
5968 lookup, using an existing branch name as a tag name is discouraged.
5969 lookup, using an existing branch name as a tag name is discouraged.
5969
5970
5970 Returns 0 on success.
5971 Returns 0 on success.
5971 """
5972 """
5972 opts = pycompat.byteskwargs(opts)
5973 opts = pycompat.byteskwargs(opts)
5973 with repo.wlock(), repo.lock():
5974 with repo.wlock(), repo.lock():
5974 rev_ = "."
5975 rev_ = "."
5975 names = [t.strip() for t in (name1,) + names]
5976 names = [t.strip() for t in (name1,) + names]
5976 if len(names) != len(set(names)):
5977 if len(names) != len(set(names)):
5977 raise error.Abort(_('tag names must be unique'))
5978 raise error.Abort(_('tag names must be unique'))
5978 for n in names:
5979 for n in names:
5979 scmutil.checknewlabel(repo, n, 'tag')
5980 scmutil.checknewlabel(repo, n, 'tag')
5980 if not n:
5981 if not n:
5981 raise error.Abort(_('tag names cannot consist entirely of '
5982 raise error.Abort(_('tag names cannot consist entirely of '
5982 'whitespace'))
5983 'whitespace'))
5983 if opts.get('rev') and opts.get('remove'):
5984 if opts.get('rev') and opts.get('remove'):
5984 raise error.Abort(_("--rev and --remove are incompatible"))
5985 raise error.Abort(_("--rev and --remove are incompatible"))
5985 if opts.get('rev'):
5986 if opts.get('rev'):
5986 rev_ = opts['rev']
5987 rev_ = opts['rev']
5987 message = opts.get('message')
5988 message = opts.get('message')
5988 if opts.get('remove'):
5989 if opts.get('remove'):
5989 if opts.get('local'):
5990 if opts.get('local'):
5990 expectedtype = 'local'
5991 expectedtype = 'local'
5991 else:
5992 else:
5992 expectedtype = 'global'
5993 expectedtype = 'global'
5993
5994
5994 for n in names:
5995 for n in names:
5995 if repo.tagtype(n) == 'global':
5996 if repo.tagtype(n) == 'global':
5996 alltags = tagsmod.findglobaltags(ui, repo)
5997 alltags = tagsmod.findglobaltags(ui, repo)
5997 if alltags[n][0] == nullid:
5998 if alltags[n][0] == nullid:
5998 raise error.Abort(_("tag '%s' is already removed") % n)
5999 raise error.Abort(_("tag '%s' is already removed") % n)
5999 if not repo.tagtype(n):
6000 if not repo.tagtype(n):
6000 raise error.Abort(_("tag '%s' does not exist") % n)
6001 raise error.Abort(_("tag '%s' does not exist") % n)
6001 if repo.tagtype(n) != expectedtype:
6002 if repo.tagtype(n) != expectedtype:
6002 if expectedtype == 'global':
6003 if expectedtype == 'global':
6003 raise error.Abort(_("tag '%s' is not a global tag") % n)
6004 raise error.Abort(_("tag '%s' is not a global tag") % n)
6004 else:
6005 else:
6005 raise error.Abort(_("tag '%s' is not a local tag") % n)
6006 raise error.Abort(_("tag '%s' is not a local tag") % n)
6006 rev_ = 'null'
6007 rev_ = 'null'
6007 if not message:
6008 if not message:
6008 # we don't translate commit messages
6009 # we don't translate commit messages
6009 message = 'Removed tag %s' % ', '.join(names)
6010 message = 'Removed tag %s' % ', '.join(names)
6010 elif not opts.get('force'):
6011 elif not opts.get('force'):
6011 for n in names:
6012 for n in names:
6012 if n in repo.tags():
6013 if n in repo.tags():
6013 raise error.Abort(_("tag '%s' already exists "
6014 raise error.Abort(_("tag '%s' already exists "
6014 "(use -f to force)") % n)
6015 "(use -f to force)") % n)
6015 if not opts.get('local'):
6016 if not opts.get('local'):
6016 p1, p2 = repo.dirstate.parents()
6017 p1, p2 = repo.dirstate.parents()
6017 if p2 != nullid:
6018 if p2 != nullid:
6018 raise error.Abort(_('uncommitted merge'))
6019 raise error.Abort(_('uncommitted merge'))
6019 bheads = repo.branchheads()
6020 bheads = repo.branchheads()
6020 if not opts.get('force') and bheads and p1 not in bheads:
6021 if not opts.get('force') and bheads and p1 not in bheads:
6021 raise error.Abort(_('working directory is not at a branch head '
6022 raise error.Abort(_('working directory is not at a branch head '
6022 '(use -f to force)'))
6023 '(use -f to force)'))
6023 node = scmutil.revsingle(repo, rev_).node()
6024 node = scmutil.revsingle(repo, rev_).node()
6024
6025
6025 if not message:
6026 if not message:
6026 # we don't translate commit messages
6027 # we don't translate commit messages
6027 message = ('Added tag %s for changeset %s' %
6028 message = ('Added tag %s for changeset %s' %
6028 (', '.join(names), short(node)))
6029 (', '.join(names), short(node)))
6029
6030
6030 date = opts.get('date')
6031 date = opts.get('date')
6031 if date:
6032 if date:
6032 date = dateutil.parsedate(date)
6033 date = dateutil.parsedate(date)
6033
6034
6034 if opts.get('remove'):
6035 if opts.get('remove'):
6035 editform = 'tag.remove'
6036 editform = 'tag.remove'
6036 else:
6037 else:
6037 editform = 'tag.add'
6038 editform = 'tag.add'
6038 editor = cmdutil.getcommiteditor(editform=editform,
6039 editor = cmdutil.getcommiteditor(editform=editform,
6039 **pycompat.strkwargs(opts))
6040 **pycompat.strkwargs(opts))
6040
6041
6041 # don't allow tagging the null rev
6042 # don't allow tagging the null rev
6042 if (not opts.get('remove') and
6043 if (not opts.get('remove') and
6043 scmutil.revsingle(repo, rev_).rev() == nullrev):
6044 scmutil.revsingle(repo, rev_).rev() == nullrev):
6044 raise error.Abort(_("cannot tag null revision"))
6045 raise error.Abort(_("cannot tag null revision"))
6045
6046
6046 tagsmod.tag(repo, names, node, message, opts.get('local'),
6047 tagsmod.tag(repo, names, node, message, opts.get('local'),
6047 opts.get('user'), date, editor=editor)
6048 opts.get('user'), date, editor=editor)
6048
6049
6049 @command(
6050 @command(
6050 'tags', formatteropts, '',
6051 'tags', formatteropts, '',
6051 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
6052 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
6052 intents={INTENT_READONLY})
6053 intents={INTENT_READONLY})
6053 def tags(ui, repo, **opts):
6054 def tags(ui, repo, **opts):
6054 """list repository tags
6055 """list repository tags
6055
6056
6056 This lists both regular and local tags. When the -v/--verbose
6057 This lists both regular and local tags. When the -v/--verbose
6057 switch is used, a third column "local" is printed for local tags.
6058 switch is used, a third column "local" is printed for local tags.
6058 When the -q/--quiet switch is used, only the tag name is printed.
6059 When the -q/--quiet switch is used, only the tag name is printed.
6059
6060
6060 .. container:: verbose
6061 .. container:: verbose
6061
6062
6062 Template:
6063 Template:
6063
6064
6064 The following keywords are supported in addition to the common template
6065 The following keywords are supported in addition to the common template
6065 keywords and functions such as ``{tag}``. See also
6066 keywords and functions such as ``{tag}``. See also
6066 :hg:`help templates`.
6067 :hg:`help templates`.
6067
6068
6068 :type: String. ``local`` for local tags.
6069 :type: String. ``local`` for local tags.
6069
6070
6070 Returns 0 on success.
6071 Returns 0 on success.
6071 """
6072 """
6072
6073
6073 opts = pycompat.byteskwargs(opts)
6074 opts = pycompat.byteskwargs(opts)
6074 ui.pager('tags')
6075 ui.pager('tags')
6075 fm = ui.formatter('tags', opts)
6076 fm = ui.formatter('tags', opts)
6076 hexfunc = fm.hexfunc
6077 hexfunc = fm.hexfunc
6077
6078
6078 for t, n in reversed(repo.tagslist()):
6079 for t, n in reversed(repo.tagslist()):
6079 hn = hexfunc(n)
6080 hn = hexfunc(n)
6080 label = 'tags.normal'
6081 label = 'tags.normal'
6081 tagtype = ''
6082 tagtype = ''
6082 if repo.tagtype(t) == 'local':
6083 if repo.tagtype(t) == 'local':
6083 label = 'tags.local'
6084 label = 'tags.local'
6084 tagtype = 'local'
6085 tagtype = 'local'
6085
6086
6086 fm.startitem()
6087 fm.startitem()
6087 fm.context(repo=repo)
6088 fm.context(repo=repo)
6088 fm.write('tag', '%s', t, label=label)
6089 fm.write('tag', '%s', t, label=label)
6089 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6090 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6090 fm.condwrite(not ui.quiet, 'rev node', fmt,
6091 fm.condwrite(not ui.quiet, 'rev node', fmt,
6091 repo.changelog.rev(n), hn, label=label)
6092 repo.changelog.rev(n), hn, label=label)
6092 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6093 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6093 tagtype, label=label)
6094 tagtype, label=label)
6094 fm.plain('\n')
6095 fm.plain('\n')
6095 fm.end()
6096 fm.end()
6096
6097
6097 @command('tip',
6098 @command('tip',
6098 [('p', 'patch', None, _('show patch')),
6099 [('p', 'patch', None, _('show patch')),
6099 ('g', 'git', None, _('use git extended diff format')),
6100 ('g', 'git', None, _('use git extended diff format')),
6100 ] + templateopts,
6101 ] + templateopts,
6101 _('[-p] [-g]'),
6102 _('[-p] [-g]'),
6102 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
6103 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
6103 def tip(ui, repo, **opts):
6104 def tip(ui, repo, **opts):
6104 """show the tip revision (DEPRECATED)
6105 """show the tip revision (DEPRECATED)
6105
6106
6106 The tip revision (usually just called the tip) is the changeset
6107 The tip revision (usually just called the tip) is the changeset
6107 most recently added to the repository (and therefore the most
6108 most recently added to the repository (and therefore the most
6108 recently changed head).
6109 recently changed head).
6109
6110
6110 If you have just made a commit, that commit will be the tip. If
6111 If you have just made a commit, that commit will be the tip. If
6111 you have just pulled changes from another repository, the tip of
6112 you have just pulled changes from another repository, the tip of
6112 that repository becomes the current tip. The "tip" tag is special
6113 that repository becomes the current tip. The "tip" tag is special
6113 and cannot be renamed or assigned to a different changeset.
6114 and cannot be renamed or assigned to a different changeset.
6114
6115
6115 This command is deprecated, please use :hg:`heads` instead.
6116 This command is deprecated, please use :hg:`heads` instead.
6116
6117
6117 Returns 0 on success.
6118 Returns 0 on success.
6118 """
6119 """
6119 opts = pycompat.byteskwargs(opts)
6120 opts = pycompat.byteskwargs(opts)
6120 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
6121 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
6121 displayer.show(repo['tip'])
6122 displayer.show(repo['tip'])
6122 displayer.close()
6123 displayer.close()
6123
6124
6124 @command('unbundle',
6125 @command('unbundle',
6125 [('u', 'update', None,
6126 [('u', 'update', None,
6126 _('update to new branch head if changesets were unbundled'))],
6127 _('update to new branch head if changesets were unbundled'))],
6127 _('[-u] FILE...'),
6128 _('[-u] FILE...'),
6128 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6129 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6129 def unbundle(ui, repo, fname1, *fnames, **opts):
6130 def unbundle(ui, repo, fname1, *fnames, **opts):
6130 """apply one or more bundle files
6131 """apply one or more bundle files
6131
6132
6132 Apply one or more bundle files generated by :hg:`bundle`.
6133 Apply one or more bundle files generated by :hg:`bundle`.
6133
6134
6134 Returns 0 on success, 1 if an update has unresolved files.
6135 Returns 0 on success, 1 if an update has unresolved files.
6135 """
6136 """
6136 fnames = (fname1,) + fnames
6137 fnames = (fname1,) + fnames
6137
6138
6138 with repo.lock():
6139 with repo.lock():
6139 for fname in fnames:
6140 for fname in fnames:
6140 f = hg.openpath(ui, fname)
6141 f = hg.openpath(ui, fname)
6141 gen = exchange.readbundle(ui, f, fname)
6142 gen = exchange.readbundle(ui, f, fname)
6142 if isinstance(gen, streamclone.streamcloneapplier):
6143 if isinstance(gen, streamclone.streamcloneapplier):
6143 raise error.Abort(
6144 raise error.Abort(
6144 _('packed bundles cannot be applied with '
6145 _('packed bundles cannot be applied with '
6145 '"hg unbundle"'),
6146 '"hg unbundle"'),
6146 hint=_('use "hg debugapplystreamclonebundle"'))
6147 hint=_('use "hg debugapplystreamclonebundle"'))
6147 url = 'bundle:' + fname
6148 url = 'bundle:' + fname
6148 try:
6149 try:
6149 txnname = 'unbundle'
6150 txnname = 'unbundle'
6150 if not isinstance(gen, bundle2.unbundle20):
6151 if not isinstance(gen, bundle2.unbundle20):
6151 txnname = 'unbundle\n%s' % util.hidepassword(url)
6152 txnname = 'unbundle\n%s' % util.hidepassword(url)
6152 with repo.transaction(txnname) as tr:
6153 with repo.transaction(txnname) as tr:
6153 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6154 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6154 url=url)
6155 url=url)
6155 except error.BundleUnknownFeatureError as exc:
6156 except error.BundleUnknownFeatureError as exc:
6156 raise error.Abort(
6157 raise error.Abort(
6157 _('%s: unknown bundle feature, %s') % (fname, exc),
6158 _('%s: unknown bundle feature, %s') % (fname, exc),
6158 hint=_("see https://mercurial-scm.org/"
6159 hint=_("see https://mercurial-scm.org/"
6159 "wiki/BundleFeature for more "
6160 "wiki/BundleFeature for more "
6160 "information"))
6161 "information"))
6161 modheads = bundle2.combinechangegroupresults(op)
6162 modheads = bundle2.combinechangegroupresults(op)
6162
6163
6163 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6164 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6164
6165
6165 @command('unshelve',
6166 @command('unshelve',
6166 [('a', 'abort', None,
6167 [('a', 'abort', None,
6167 _('abort an incomplete unshelve operation')),
6168 _('abort an incomplete unshelve operation')),
6168 ('c', 'continue', None,
6169 ('c', 'continue', None,
6169 _('continue an incomplete unshelve operation')),
6170 _('continue an incomplete unshelve operation')),
6170 ('i', 'interactive', None,
6171 ('i', 'interactive', None,
6171 _('use interactive mode (EXPERIMENTAL)')),
6172 _('use interactive mode (EXPERIMENTAL)')),
6172 ('k', 'keep', None,
6173 ('k', 'keep', None,
6173 _('keep shelve after unshelving')),
6174 _('keep shelve after unshelving')),
6174 ('n', 'name', '',
6175 ('n', 'name', '',
6175 _('restore shelved change with given name'), _('NAME')),
6176 _('restore shelved change with given name'), _('NAME')),
6176 ('t', 'tool', '', _('specify merge tool')),
6177 ('t', 'tool', '', _('specify merge tool')),
6177 ('', 'date', '',
6178 ('', 'date', '',
6178 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
6179 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
6179 _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
6180 _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
6180 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
6181 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
6181 def unshelve(ui, repo, *shelved, **opts):
6182 def unshelve(ui, repo, *shelved, **opts):
6182 """restore a shelved change to the working directory
6183 """restore a shelved change to the working directory
6183
6184
6184 This command accepts an optional name of a shelved change to
6185 This command accepts an optional name of a shelved change to
6185 restore. If none is given, the most recent shelved change is used.
6186 restore. If none is given, the most recent shelved change is used.
6186
6187
6187 If a shelved change is applied successfully, the bundle that
6188 If a shelved change is applied successfully, the bundle that
6188 contains the shelved changes is moved to a backup location
6189 contains the shelved changes is moved to a backup location
6189 (.hg/shelve-backup).
6190 (.hg/shelve-backup).
6190
6191
6191 Since you can restore a shelved change on top of an arbitrary
6192 Since you can restore a shelved change on top of an arbitrary
6192 commit, it is possible that unshelving will result in a conflict
6193 commit, it is possible that unshelving will result in a conflict
6193 between your changes and the commits you are unshelving onto. If
6194 between your changes and the commits you are unshelving onto. If
6194 this occurs, you must resolve the conflict, then use
6195 this occurs, you must resolve the conflict, then use
6195 ``--continue`` to complete the unshelve operation. (The bundle
6196 ``--continue`` to complete the unshelve operation. (The bundle
6196 will not be moved until you successfully complete the unshelve.)
6197 will not be moved until you successfully complete the unshelve.)
6197
6198
6198 (Alternatively, you can use ``--abort`` to abandon an unshelve
6199 (Alternatively, you can use ``--abort`` to abandon an unshelve
6199 that causes a conflict. This reverts the unshelved changes, and
6200 that causes a conflict. This reverts the unshelved changes, and
6200 leaves the bundle in place.)
6201 leaves the bundle in place.)
6201
6202
6202 If bare shelved change (when no files are specified, without interactive,
6203 If bare shelved change (when no files are specified, without interactive,
6203 include and exclude option) was done on newly created branch it would
6204 include and exclude option) was done on newly created branch it would
6204 restore branch information to the working directory.
6205 restore branch information to the working directory.
6205
6206
6206 After a successful unshelve, the shelved changes are stored in a
6207 After a successful unshelve, the shelved changes are stored in a
6207 backup directory. Only the N most recent backups are kept. N
6208 backup directory. Only the N most recent backups are kept. N
6208 defaults to 10 but can be overridden using the ``shelve.maxbackups``
6209 defaults to 10 but can be overridden using the ``shelve.maxbackups``
6209 configuration option.
6210 configuration option.
6210
6211
6211 .. container:: verbose
6212 .. container:: verbose
6212
6213
6213 Timestamp in seconds is used to decide order of backups. More
6214 Timestamp in seconds is used to decide order of backups. More
6214 than ``maxbackups`` backups are kept, if same timestamp
6215 than ``maxbackups`` backups are kept, if same timestamp
6215 prevents from deciding exact order of them, for safety.
6216 prevents from deciding exact order of them, for safety.
6216
6217
6217 Selected changes can be unshelved with ``--interactive`` flag.
6218 Selected changes can be unshelved with ``--interactive`` flag.
6218 The working directory is updated with the selected changes, and
6219 The working directory is updated with the selected changes, and
6219 only the unselected changes remain shelved.
6220 only the unselected changes remain shelved.
6220 Note: The whole shelve is applied to working directory first before
6221 Note: The whole shelve is applied to working directory first before
6221 running interactively. So, this will bring up all the conflicts between
6222 running interactively. So, this will bring up all the conflicts between
6222 working directory and the shelve, irrespective of which changes will be
6223 working directory and the shelve, irrespective of which changes will be
6223 unshelved.
6224 unshelved.
6224 """
6225 """
6225 with repo.wlock():
6226 with repo.wlock():
6226 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
6227 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
6227
6228
6228 statemod.addunfinished(
6229 statemod.addunfinished(
6229 'unshelve', fname='shelvedstate', continueflag=True,
6230 'unshelve', fname='shelvedstate', continueflag=True,
6230 abortfunc=shelvemod.hgabortunshelve,
6231 abortfunc=shelvemod.hgabortunshelve,
6231 continuefunc=shelvemod.hgcontinueunshelve,
6232 continuefunc=shelvemod.hgcontinueunshelve,
6232 cmdmsg=_('unshelve already in progress'),
6233 cmdmsg=_('unshelve already in progress'),
6233 )
6234 )
6234
6235
6235 @command('update|up|checkout|co',
6236 @command('update|up|checkout|co',
6236 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6237 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6237 ('c', 'check', None, _('require clean working directory')),
6238 ('c', 'check', None, _('require clean working directory')),
6238 ('m', 'merge', None, _('merge uncommitted changes')),
6239 ('m', 'merge', None, _('merge uncommitted changes')),
6239 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6240 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6240 ('r', 'rev', '', _('revision'), _('REV'))
6241 ('r', 'rev', '', _('revision'), _('REV'))
6241 ] + mergetoolopts,
6242 ] + mergetoolopts,
6242 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6243 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6243 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6244 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6244 helpbasic=True)
6245 helpbasic=True)
6245 def update(ui, repo, node=None, **opts):
6246 def update(ui, repo, node=None, **opts):
6246 """update working directory (or switch revisions)
6247 """update working directory (or switch revisions)
6247
6248
6248 Update the repository's working directory to the specified
6249 Update the repository's working directory to the specified
6249 changeset. If no changeset is specified, update to the tip of the
6250 changeset. If no changeset is specified, update to the tip of the
6250 current named branch and move the active bookmark (see :hg:`help
6251 current named branch and move the active bookmark (see :hg:`help
6251 bookmarks`).
6252 bookmarks`).
6252
6253
6253 Update sets the working directory's parent revision to the specified
6254 Update sets the working directory's parent revision to the specified
6254 changeset (see :hg:`help parents`).
6255 changeset (see :hg:`help parents`).
6255
6256
6256 If the changeset is not a descendant or ancestor of the working
6257 If the changeset is not a descendant or ancestor of the working
6257 directory's parent and there are uncommitted changes, the update is
6258 directory's parent and there are uncommitted changes, the update is
6258 aborted. With the -c/--check option, the working directory is checked
6259 aborted. With the -c/--check option, the working directory is checked
6259 for uncommitted changes; if none are found, the working directory is
6260 for uncommitted changes; if none are found, the working directory is
6260 updated to the specified changeset.
6261 updated to the specified changeset.
6261
6262
6262 .. container:: verbose
6263 .. container:: verbose
6263
6264
6264 The -C/--clean, -c/--check, and -m/--merge options control what
6265 The -C/--clean, -c/--check, and -m/--merge options control what
6265 happens if the working directory contains uncommitted changes.
6266 happens if the working directory contains uncommitted changes.
6266 At most of one of them can be specified.
6267 At most of one of them can be specified.
6267
6268
6268 1. If no option is specified, and if
6269 1. If no option is specified, and if
6269 the requested changeset is an ancestor or descendant of
6270 the requested changeset is an ancestor or descendant of
6270 the working directory's parent, the uncommitted changes
6271 the working directory's parent, the uncommitted changes
6271 are merged into the requested changeset and the merged
6272 are merged into the requested changeset and the merged
6272 result is left uncommitted. If the requested changeset is
6273 result is left uncommitted. If the requested changeset is
6273 not an ancestor or descendant (that is, it is on another
6274 not an ancestor or descendant (that is, it is on another
6274 branch), the update is aborted and the uncommitted changes
6275 branch), the update is aborted and the uncommitted changes
6275 are preserved.
6276 are preserved.
6276
6277
6277 2. With the -m/--merge option, the update is allowed even if the
6278 2. With the -m/--merge option, the update is allowed even if the
6278 requested changeset is not an ancestor or descendant of
6279 requested changeset is not an ancestor or descendant of
6279 the working directory's parent.
6280 the working directory's parent.
6280
6281
6281 3. With the -c/--check option, the update is aborted and the
6282 3. With the -c/--check option, the update is aborted and the
6282 uncommitted changes are preserved.
6283 uncommitted changes are preserved.
6283
6284
6284 4. With the -C/--clean option, uncommitted changes are discarded and
6285 4. With the -C/--clean option, uncommitted changes are discarded and
6285 the working directory is updated to the requested changeset.
6286 the working directory is updated to the requested changeset.
6286
6287
6287 To cancel an uncommitted merge (and lose your changes), use
6288 To cancel an uncommitted merge (and lose your changes), use
6288 :hg:`merge --abort`.
6289 :hg:`merge --abort`.
6289
6290
6290 Use null as the changeset to remove the working directory (like
6291 Use null as the changeset to remove the working directory (like
6291 :hg:`clone -U`).
6292 :hg:`clone -U`).
6292
6293
6293 If you want to revert just one file to an older revision, use
6294 If you want to revert just one file to an older revision, use
6294 :hg:`revert [-r REV] NAME`.
6295 :hg:`revert [-r REV] NAME`.
6295
6296
6296 See :hg:`help dates` for a list of formats valid for -d/--date.
6297 See :hg:`help dates` for a list of formats valid for -d/--date.
6297
6298
6298 Returns 0 on success, 1 if there are unresolved files.
6299 Returns 0 on success, 1 if there are unresolved files.
6299 """
6300 """
6300 rev = opts.get(r'rev')
6301 rev = opts.get(r'rev')
6301 date = opts.get(r'date')
6302 date = opts.get(r'date')
6302 clean = opts.get(r'clean')
6303 clean = opts.get(r'clean')
6303 check = opts.get(r'check')
6304 check = opts.get(r'check')
6304 merge = opts.get(r'merge')
6305 merge = opts.get(r'merge')
6305 if rev and node:
6306 if rev and node:
6306 raise error.Abort(_("please specify just one revision"))
6307 raise error.Abort(_("please specify just one revision"))
6307
6308
6308 if ui.configbool('commands', 'update.requiredest'):
6309 if ui.configbool('commands', 'update.requiredest'):
6309 if not node and not rev and not date:
6310 if not node and not rev and not date:
6310 raise error.Abort(_('you must specify a destination'),
6311 raise error.Abort(_('you must specify a destination'),
6311 hint=_('for example: hg update ".::"'))
6312 hint=_('for example: hg update ".::"'))
6312
6313
6313 if rev is None or rev == '':
6314 if rev is None or rev == '':
6314 rev = node
6315 rev = node
6315
6316
6316 if date and rev is not None:
6317 if date and rev is not None:
6317 raise error.Abort(_("you can't specify a revision and a date"))
6318 raise error.Abort(_("you can't specify a revision and a date"))
6318
6319
6319 if len([x for x in (clean, check, merge) if x]) > 1:
6320 if len([x for x in (clean, check, merge) if x]) > 1:
6320 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6321 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6321 "or -m/--merge"))
6322 "or -m/--merge"))
6322
6323
6323 updatecheck = None
6324 updatecheck = None
6324 if check:
6325 if check:
6325 updatecheck = 'abort'
6326 updatecheck = 'abort'
6326 elif merge:
6327 elif merge:
6327 updatecheck = 'none'
6328 updatecheck = 'none'
6328
6329
6329 with repo.wlock():
6330 with repo.wlock():
6330 cmdutil.clearunfinished(repo)
6331 cmdutil.clearunfinished(repo)
6331 if date:
6332 if date:
6332 rev = cmdutil.finddate(ui, repo, date)
6333 rev = cmdutil.finddate(ui, repo, date)
6333
6334
6334 # if we defined a bookmark, we have to remember the original name
6335 # if we defined a bookmark, we have to remember the original name
6335 brev = rev
6336 brev = rev
6336 if rev:
6337 if rev:
6337 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6338 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6338 ctx = scmutil.revsingle(repo, rev, default=None)
6339 ctx = scmutil.revsingle(repo, rev, default=None)
6339 rev = ctx.rev()
6340 rev = ctx.rev()
6340 hidden = ctx.hidden()
6341 hidden = ctx.hidden()
6341 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6342 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6342 with ui.configoverride(overrides, 'update'):
6343 with ui.configoverride(overrides, 'update'):
6343 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6344 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6344 updatecheck=updatecheck)
6345 updatecheck=updatecheck)
6345 if hidden:
6346 if hidden:
6346 ctxstr = ctx.hex()[:12]
6347 ctxstr = ctx.hex()[:12]
6347 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6348 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6348
6349
6349 if ctx.obsolete():
6350 if ctx.obsolete():
6350 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6351 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6351 ui.warn("(%s)\n" % obsfatemsg)
6352 ui.warn("(%s)\n" % obsfatemsg)
6352 return ret
6353 return ret
6353
6354
6354 @command('verify',
6355 @command('verify',
6355 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6356 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6356 helpcategory=command.CATEGORY_MAINTENANCE)
6357 helpcategory=command.CATEGORY_MAINTENANCE)
6357 def verify(ui, repo, **opts):
6358 def verify(ui, repo, **opts):
6358 """verify the integrity of the repository
6359 """verify the integrity of the repository
6359
6360
6360 Verify the integrity of the current repository.
6361 Verify the integrity of the current repository.
6361
6362
6362 This will perform an extensive check of the repository's
6363 This will perform an extensive check of the repository's
6363 integrity, validating the hashes and checksums of each entry in
6364 integrity, validating the hashes and checksums of each entry in
6364 the changelog, manifest, and tracked files, as well as the
6365 the changelog, manifest, and tracked files, as well as the
6365 integrity of their crosslinks and indices.
6366 integrity of their crosslinks and indices.
6366
6367
6367 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6368 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6368 for more information about recovery from corruption of the
6369 for more information about recovery from corruption of the
6369 repository.
6370 repository.
6370
6371
6371 Returns 0 on success, 1 if errors are encountered.
6372 Returns 0 on success, 1 if errors are encountered.
6372 """
6373 """
6373 opts = pycompat.byteskwargs(opts)
6374 opts = pycompat.byteskwargs(opts)
6374
6375
6375 level = None
6376 level = None
6376 if opts['full']:
6377 if opts['full']:
6377 level = verifymod.VERIFY_FULL
6378 level = verifymod.VERIFY_FULL
6378 return hg.verify(repo, level)
6379 return hg.verify(repo, level)
6379
6380
6380 @command(
6381 @command(
6381 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6382 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6382 norepo=True, intents={INTENT_READONLY})
6383 norepo=True, intents={INTENT_READONLY})
6383 def version_(ui, **opts):
6384 def version_(ui, **opts):
6384 """output version and copyright information
6385 """output version and copyright information
6385
6386
6386 .. container:: verbose
6387 .. container:: verbose
6387
6388
6388 Template:
6389 Template:
6389
6390
6390 The following keywords are supported. See also :hg:`help templates`.
6391 The following keywords are supported. See also :hg:`help templates`.
6391
6392
6392 :extensions: List of extensions.
6393 :extensions: List of extensions.
6393 :ver: String. Version number.
6394 :ver: String. Version number.
6394
6395
6395 And each entry of ``{extensions}`` provides the following sub-keywords
6396 And each entry of ``{extensions}`` provides the following sub-keywords
6396 in addition to ``{ver}``.
6397 in addition to ``{ver}``.
6397
6398
6398 :bundled: Boolean. True if included in the release.
6399 :bundled: Boolean. True if included in the release.
6399 :name: String. Extension name.
6400 :name: String. Extension name.
6400 """
6401 """
6401 opts = pycompat.byteskwargs(opts)
6402 opts = pycompat.byteskwargs(opts)
6402 if ui.verbose:
6403 if ui.verbose:
6403 ui.pager('version')
6404 ui.pager('version')
6404 fm = ui.formatter("version", opts)
6405 fm = ui.formatter("version", opts)
6405 fm.startitem()
6406 fm.startitem()
6406 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6407 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6407 util.version())
6408 util.version())
6408 license = _(
6409 license = _(
6409 "(see https://mercurial-scm.org for more information)\n"
6410 "(see https://mercurial-scm.org for more information)\n"
6410 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6411 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6411 "This is free software; see the source for copying conditions. "
6412 "This is free software; see the source for copying conditions. "
6412 "There is NO\nwarranty; "
6413 "There is NO\nwarranty; "
6413 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6414 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6414 )
6415 )
6415 if not ui.quiet:
6416 if not ui.quiet:
6416 fm.plain(license)
6417 fm.plain(license)
6417
6418
6418 if ui.verbose:
6419 if ui.verbose:
6419 fm.plain(_("\nEnabled extensions:\n\n"))
6420 fm.plain(_("\nEnabled extensions:\n\n"))
6420 # format names and versions into columns
6421 # format names and versions into columns
6421 names = []
6422 names = []
6422 vers = []
6423 vers = []
6423 isinternals = []
6424 isinternals = []
6424 for name, module in extensions.extensions():
6425 for name, module in extensions.extensions():
6425 names.append(name)
6426 names.append(name)
6426 vers.append(extensions.moduleversion(module) or None)
6427 vers.append(extensions.moduleversion(module) or None)
6427 isinternals.append(extensions.ismoduleinternal(module))
6428 isinternals.append(extensions.ismoduleinternal(module))
6428 fn = fm.nested("extensions", tmpl='{name}\n')
6429 fn = fm.nested("extensions", tmpl='{name}\n')
6429 if names:
6430 if names:
6430 namefmt = " %%-%ds " % max(len(n) for n in names)
6431 namefmt = " %%-%ds " % max(len(n) for n in names)
6431 places = [_("external"), _("internal")]
6432 places = [_("external"), _("internal")]
6432 for n, v, p in zip(names, vers, isinternals):
6433 for n, v, p in zip(names, vers, isinternals):
6433 fn.startitem()
6434 fn.startitem()
6434 fn.condwrite(ui.verbose, "name", namefmt, n)
6435 fn.condwrite(ui.verbose, "name", namefmt, n)
6435 if ui.verbose:
6436 if ui.verbose:
6436 fn.plain("%s " % places[p])
6437 fn.plain("%s " % places[p])
6437 fn.data(bundled=p)
6438 fn.data(bundled=p)
6438 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6439 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6439 if ui.verbose:
6440 if ui.verbose:
6440 fn.plain("\n")
6441 fn.plain("\n")
6441 fn.end()
6442 fn.end()
6442 fm.end()
6443 fm.end()
6443
6444
6444 def loadcmdtable(ui, name, cmdtable):
6445 def loadcmdtable(ui, name, cmdtable):
6445 """Load command functions from specified cmdtable
6446 """Load command functions from specified cmdtable
6446 """
6447 """
6447 overrides = [cmd for cmd in cmdtable if cmd in table]
6448 overrides = [cmd for cmd in cmdtable if cmd in table]
6448 if overrides:
6449 if overrides:
6449 ui.warn(_("extension '%s' overrides commands: %s\n")
6450 ui.warn(_("extension '%s' overrides commands: %s\n")
6450 % (name, " ".join(overrides)))
6451 % (name, " ".join(overrides)))
6451 table.update(cmdtable)
6452 table.update(cmdtable)
@@ -1,609 +1,622 b''
1 #require serve
1 #require serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo>foo
5 $ echo foo>foo
6 $ hg commit -Am 1 -d '1 0'
6 $ hg commit -Am 1 -d '1 0'
7 adding foo
7 adding foo
8 $ echo bar>bar
8 $ echo bar>bar
9 $ hg commit -Am 2 -d '2 0'
9 $ hg commit -Am 2 -d '2 0'
10 adding bar
10 adding bar
11 $ mkdir baz
11 $ mkdir baz
12 $ echo bletch>baz/bletch
12 $ echo bletch>baz/bletch
13 $ hg commit -Am 3 -d '1000000000 0'
13 $ hg commit -Am 3 -d '1000000000 0'
14 adding baz/bletch
14 adding baz/bletch
15 $ hg init subrepo
15 $ hg init subrepo
16 $ touch subrepo/sub
16 $ touch subrepo/sub
17 $ hg -q -R subrepo ci -Am "init subrepo"
17 $ hg -q -R subrepo ci -Am "init subrepo"
18 $ echo "subrepo = subrepo" > .hgsub
18 $ echo "subrepo = subrepo" > .hgsub
19 $ hg add .hgsub
19 $ hg add .hgsub
20 $ hg ci -m "add subrepo"
20 $ hg ci -m "add subrepo"
21
21
22 $ cat >> $HGRCPATH <<EOF
22 $ cat >> $HGRCPATH <<EOF
23 > [extensions]
23 > [extensions]
24 > share =
24 > share =
25 > EOF
25 > EOF
26
26
27 hg subrepos are shared when the parent repo is shared
27 hg subrepos are shared when the parent repo is shared
28
28
29 $ cd ..
29 $ cd ..
30 $ hg share test shared1
30 $ hg share test shared1
31 updating working directory
31 updating working directory
32 sharing subrepo subrepo from $TESTTMP/test/subrepo
32 sharing subrepo subrepo from $TESTTMP/test/subrepo
33 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 $ cat shared1/subrepo/.hg/sharedpath
34 $ cat shared1/subrepo/.hg/sharedpath
35 $TESTTMP/test/subrepo/.hg (no-eol)
35 $TESTTMP/test/subrepo/.hg (no-eol)
36
36
37 hg subrepos are shared into existence on demand if the parent was shared
37 hg subrepos are shared into existence on demand if the parent was shared
38
38
39 $ hg clone -qr 1 test clone1
39 $ hg clone -qr 1 test clone1
40 $ hg share clone1 share2
40 $ hg share clone1 share2
41 updating working directory
41 updating working directory
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ hg -R clone1 -q pull
43 $ hg -R clone1 -q pull
44 $ hg -R share2 update tip
44 $ hg -R share2 update tip
45 sharing subrepo subrepo from $TESTTMP/test/subrepo
45 sharing subrepo subrepo from $TESTTMP/test/subrepo
46 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ cat share2/subrepo/.hg/sharedpath
47 $ cat share2/subrepo/.hg/sharedpath
48 $TESTTMP/test/subrepo/.hg (no-eol)
48 $TESTTMP/test/subrepo/.hg (no-eol)
49 $ echo 'mod' > share2/subrepo/sub
49 $ echo 'mod' > share2/subrepo/sub
50 $ hg -R share2 ci -Sqm 'subrepo mod'
50 $ hg -R share2 ci -Sqm 'subrepo mod'
51 $ hg -R clone1 update -C tip
51 $ hg -R clone1 update -C tip
52 cloning subrepo subrepo from $TESTTMP/test/subrepo
52 cloning subrepo subrepo from $TESTTMP/test/subrepo
53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 $ find share2 | egrep 'sharedpath|00.+\.i' | sort
54 $ find share2 | egrep 'sharedpath|00.+\.i' | sort
55 share2/.hg/sharedpath
55 share2/.hg/sharedpath
56 share2/subrepo/.hg/sharedpath
56 share2/subrepo/.hg/sharedpath
57 $ hg -R share2 unshare
57 $ hg -R share2 unshare
58 unsharing subrepo 'subrepo'
58 unsharing subrepo 'subrepo'
59 $ find share2 | egrep 'sharedpath|00.+\.i' | sort
59 $ find share2 | egrep 'sharedpath|00.+\.i' | sort
60 share2/.hg/00changelog.i
60 share2/.hg/00changelog.i
61 share2/.hg/sharedpath.old
61 share2/.hg/sharedpath.old
62 share2/.hg/store/00changelog.i
62 share2/.hg/store/00changelog.i
63 share2/.hg/store/00manifest.i
63 share2/.hg/store/00manifest.i
64 share2/subrepo/.hg/00changelog.i
64 share2/subrepo/.hg/00changelog.i
65 share2/subrepo/.hg/sharedpath.old
65 share2/subrepo/.hg/sharedpath.old
66 share2/subrepo/.hg/store/00changelog.i
66 share2/subrepo/.hg/store/00changelog.i
67 share2/subrepo/.hg/store/00manifest.i
67 share2/subrepo/.hg/store/00manifest.i
68 $ hg -R share2/subrepo log -r tip -T compact
68 $ hg -R share2/subrepo log -r tip -T compact
69 1[tip] 559dcc9bfa65 1970-01-01 00:00 +0000 test
69 1[tip] 559dcc9bfa65 1970-01-01 00:00 +0000 test
70 subrepo mod
70 subrepo mod
71
71
72 $ rm -rf clone1
72 $ rm -rf clone1
73
73
74 $ hg clone -qr 1 test clone1
74 $ hg clone -qr 1 test clone1
75 $ hg share clone1 shared3
75 $ hg share clone1 shared3
76 updating working directory
76 updating working directory
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 $ hg -R clone1 -q pull
78 $ hg -R clone1 -q pull
79 $ hg -R shared3 archive --config ui.archivemeta=False -r tip -S archive
79 $ hg -R shared3 archive --config ui.archivemeta=False -r tip -S archive
80 sharing subrepo subrepo from $TESTTMP/test/subrepo
80 sharing subrepo subrepo from $TESTTMP/test/subrepo
81 $ cat shared3/subrepo/.hg/sharedpath
81 $ cat shared3/subrepo/.hg/sharedpath
82 $TESTTMP/test/subrepo/.hg (no-eol)
82 $TESTTMP/test/subrepo/.hg (no-eol)
83 $ diff -r archive test
83 $ diff -r archive test
84 Only in test: .hg
84 Only in test: .hg
85 Common subdirectories: archive/baz and test/baz (?)
85 Common subdirectories: archive/baz and test/baz (?)
86 Common subdirectories: archive/subrepo and test/subrepo (?)
86 Common subdirectories: archive/subrepo and test/subrepo (?)
87 Only in test/subrepo: .hg
87 Only in test/subrepo: .hg
88 [1]
88 [1]
89 $ rm -rf archive
89 $ rm -rf archive
90
90
91 $ cd test
91 $ cd test
92 $ echo "[web]" >> .hg/hgrc
92 $ echo "[web]" >> .hg/hgrc
93 $ echo "name = test-archive" >> .hg/hgrc
93 $ echo "name = test-archive" >> .hg/hgrc
94 $ echo "archivesubrepos = True" >> .hg/hgrc
94 $ echo "archivesubrepos = True" >> .hg/hgrc
95 $ cp .hg/hgrc .hg/hgrc-base
95 $ cp .hg/hgrc .hg/hgrc-base
96 > test_archtype() {
96 > test_archtype() {
97 > echo "allow-archive = $1" >> .hg/hgrc
97 > echo "allow-archive = $1" >> .hg/hgrc
98 > test_archtype_run "$@"
98 > test_archtype_run "$@"
99 > }
99 > }
100 > test_archtype_deprecated() {
100 > test_archtype_deprecated() {
101 > echo "allow$1 = True" >> .hg/hgrc
101 > echo "allow$1 = True" >> .hg/hgrc
102 > test_archtype_run "$@"
102 > test_archtype_run "$@"
103 > }
103 > }
104 > test_archtype_run() {
104 > test_archtype_run() {
105 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log \
105 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log \
106 > --config extensions.blackbox= --config blackbox.track=develwarn
106 > --config extensions.blackbox= --config blackbox.track=develwarn
107 > cat hg.pid >> $DAEMON_PIDS
107 > cat hg.pid >> $DAEMON_PIDS
108 > echo % $1 allowed should give 200
108 > echo % $1 allowed should give 200
109 > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$2" -
109 > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$2" -
110 > f --size --sha1 body
110 > f --size --sha1 body
111 > echo % $3 and $4 disallowed should both give 403
111 > echo % $3 and $4 disallowed should both give 403
112 > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$3" -
112 > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$3" -
113 > f --size --sha1 body
113 > f --size --sha1 body
114 > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$4" -
114 > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$4" -
115 > f --size --sha1 body
115 > f --size --sha1 body
116 > killdaemons.py
116 > killdaemons.py
117 > cat errors.log
117 > cat errors.log
118 > hg blackbox --config extensions.blackbox= --config blackbox.track=
118 > hg blackbox --config extensions.blackbox= --config blackbox.track=
119 > cp .hg/hgrc-base .hg/hgrc
119 > cp .hg/hgrc-base .hg/hgrc
120 > }
120 > }
121
121
122 check http return codes
122 check http return codes
123
123
124 $ test_archtype gz tar.gz tar.bz2 zip
124 $ test_archtype gz tar.gz tar.bz2 zip
125 % gz allowed should give 200
125 % gz allowed should give 200
126 200 Script output follows
126 200 Script output follows
127 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
127 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
128 content-type: application/x-gzip
128 content-type: application/x-gzip
129 date: $HTTP_DATE$
129 date: $HTTP_DATE$
130 etag: W/"*" (glob)
130 etag: W/"*" (glob)
131 server: testing stub value
131 server: testing stub value
132 transfer-encoding: chunked
132 transfer-encoding: chunked
133
133
134 body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
134 body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
135 % tar.bz2 and zip disallowed should both give 403
135 % tar.bz2 and zip disallowed should both give 403
136 403 Archive type not allowed: bz2
136 403 Archive type not allowed: bz2
137 content-type: text/html; charset=ascii
137 content-type: text/html; charset=ascii
138 date: $HTTP_DATE$
138 date: $HTTP_DATE$
139 etag: W/"*" (glob)
139 etag: W/"*" (glob)
140 server: testing stub value
140 server: testing stub value
141 transfer-encoding: chunked
141 transfer-encoding: chunked
142
142
143 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
143 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
144 403 Archive type not allowed: zip
144 403 Archive type not allowed: zip
145 content-type: text/html; charset=ascii
145 content-type: text/html; charset=ascii
146 date: $HTTP_DATE$
146 date: $HTTP_DATE$
147 etag: W/"*" (glob)
147 etag: W/"*" (glob)
148 server: testing stub value
148 server: testing stub value
149 transfer-encoding: chunked
149 transfer-encoding: chunked
150
150
151 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
151 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
152 $ test_archtype bz2 tar.bz2 zip tar.gz
152 $ test_archtype bz2 tar.bz2 zip tar.gz
153 % bz2 allowed should give 200
153 % bz2 allowed should give 200
154 200 Script output follows
154 200 Script output follows
155 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
155 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
156 content-type: application/x-bzip2
156 content-type: application/x-bzip2
157 date: $HTTP_DATE$
157 date: $HTTP_DATE$
158 etag: W/"*" (glob)
158 etag: W/"*" (glob)
159 server: testing stub value
159 server: testing stub value
160 transfer-encoding: chunked
160 transfer-encoding: chunked
161
161
162 body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
162 body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
163 % zip and tar.gz disallowed should both give 403
163 % zip and tar.gz disallowed should both give 403
164 403 Archive type not allowed: zip
164 403 Archive type not allowed: zip
165 content-type: text/html; charset=ascii
165 content-type: text/html; charset=ascii
166 date: $HTTP_DATE$
166 date: $HTTP_DATE$
167 etag: W/"*" (glob)
167 etag: W/"*" (glob)
168 server: testing stub value
168 server: testing stub value
169 transfer-encoding: chunked
169 transfer-encoding: chunked
170
170
171 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
171 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
172 403 Archive type not allowed: gz
172 403 Archive type not allowed: gz
173 content-type: text/html; charset=ascii
173 content-type: text/html; charset=ascii
174 date: $HTTP_DATE$
174 date: $HTTP_DATE$
175 etag: W/"*" (glob)
175 etag: W/"*" (glob)
176 server: testing stub value
176 server: testing stub value
177 transfer-encoding: chunked
177 transfer-encoding: chunked
178
178
179 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
179 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
180 $ test_archtype zip zip tar.gz tar.bz2
180 $ test_archtype zip zip tar.gz tar.bz2
181 % zip allowed should give 200
181 % zip allowed should give 200
182 200 Script output follows
182 200 Script output follows
183 content-disposition: attachment; filename=test-archive-1701ef1f1510.zip
183 content-disposition: attachment; filename=test-archive-1701ef1f1510.zip
184 content-type: application/zip
184 content-type: application/zip
185 date: $HTTP_DATE$
185 date: $HTTP_DATE$
186 etag: W/"*" (glob)
186 etag: W/"*" (glob)
187 server: testing stub value
187 server: testing stub value
188 transfer-encoding: chunked
188 transfer-encoding: chunked
189
189
190 body: size=(1377|1461|1489), sha1=(677b14d3d048778d5eb5552c14a67e6192068650|be6d3983aa13dfe930361b2569291cdedd02b537|1897e496871aa89ad685a92b936f5fa0d008b9e8) (re)
190 body: size=(1377|1461|1489), sha1=(677b14d3d048778d5eb5552c14a67e6192068650|be6d3983aa13dfe930361b2569291cdedd02b537|1897e496871aa89ad685a92b936f5fa0d008b9e8) (re)
191 % tar.gz and tar.bz2 disallowed should both give 403
191 % tar.gz and tar.bz2 disallowed should both give 403
192 403 Archive type not allowed: gz
192 403 Archive type not allowed: gz
193 content-type: text/html; charset=ascii
193 content-type: text/html; charset=ascii
194 date: $HTTP_DATE$
194 date: $HTTP_DATE$
195 etag: W/"*" (glob)
195 etag: W/"*" (glob)
196 server: testing stub value
196 server: testing stub value
197 transfer-encoding: chunked
197 transfer-encoding: chunked
198
198
199 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
199 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
200 403 Archive type not allowed: bz2
200 403 Archive type not allowed: bz2
201 content-type: text/html; charset=ascii
201 content-type: text/html; charset=ascii
202 date: $HTTP_DATE$
202 date: $HTTP_DATE$
203 etag: W/"*" (glob)
203 etag: W/"*" (glob)
204 server: testing stub value
204 server: testing stub value
205 transfer-encoding: chunked
205 transfer-encoding: chunked
206
206
207 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
207 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
208
208
209 check http return codes (with deprecated option)
209 check http return codes (with deprecated option)
210
210
211 $ test_archtype_deprecated gz tar.gz tar.bz2 zip
211 $ test_archtype_deprecated gz tar.gz tar.bz2 zip
212 % gz allowed should give 200
212 % gz allowed should give 200
213 200 Script output follows
213 200 Script output follows
214 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
214 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
215 content-type: application/x-gzip
215 content-type: application/x-gzip
216 date: $HTTP_DATE$
216 date: $HTTP_DATE$
217 etag: W/"*" (glob)
217 etag: W/"*" (glob)
218 server: testing stub value
218 server: testing stub value
219 transfer-encoding: chunked
219 transfer-encoding: chunked
220
220
221 body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
221 body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
222 % tar.bz2 and zip disallowed should both give 403
222 % tar.bz2 and zip disallowed should both give 403
223 403 Archive type not allowed: bz2
223 403 Archive type not allowed: bz2
224 content-type: text/html; charset=ascii
224 content-type: text/html; charset=ascii
225 date: $HTTP_DATE$
225 date: $HTTP_DATE$
226 etag: W/"*" (glob)
226 etag: W/"*" (glob)
227 server: testing stub value
227 server: testing stub value
228 transfer-encoding: chunked
228 transfer-encoding: chunked
229
229
230 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
230 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
231 403 Archive type not allowed: zip
231 403 Archive type not allowed: zip
232 content-type: text/html; charset=ascii
232 content-type: text/html; charset=ascii
233 date: $HTTP_DATE$
233 date: $HTTP_DATE$
234 etag: W/"*" (glob)
234 etag: W/"*" (glob)
235 server: testing stub value
235 server: testing stub value
236 transfer-encoding: chunked
236 transfer-encoding: chunked
237
237
238 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
238 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
239 $ test_archtype_deprecated bz2 tar.bz2 zip tar.gz
239 $ test_archtype_deprecated bz2 tar.bz2 zip tar.gz
240 % bz2 allowed should give 200
240 % bz2 allowed should give 200
241 200 Script output follows
241 200 Script output follows
242 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
242 content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
243 content-type: application/x-bzip2
243 content-type: application/x-bzip2
244 date: $HTTP_DATE$
244 date: $HTTP_DATE$
245 etag: W/"*" (glob)
245 etag: W/"*" (glob)
246 server: testing stub value
246 server: testing stub value
247 transfer-encoding: chunked
247 transfer-encoding: chunked
248
248
249 body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
249 body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
250 % zip and tar.gz disallowed should both give 403
250 % zip and tar.gz disallowed should both give 403
251 403 Archive type not allowed: zip
251 403 Archive type not allowed: zip
252 content-type: text/html; charset=ascii
252 content-type: text/html; charset=ascii
253 date: $HTTP_DATE$
253 date: $HTTP_DATE$
254 etag: W/"*" (glob)
254 etag: W/"*" (glob)
255 server: testing stub value
255 server: testing stub value
256 transfer-encoding: chunked
256 transfer-encoding: chunked
257
257
258 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
258 body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
259 403 Archive type not allowed: gz
259 403 Archive type not allowed: gz
260 content-type: text/html; charset=ascii
260 content-type: text/html; charset=ascii
261 date: $HTTP_DATE$
261 date: $HTTP_DATE$
262 etag: W/"*" (glob)
262 etag: W/"*" (glob)
263 server: testing stub value
263 server: testing stub value
264 transfer-encoding: chunked
264 transfer-encoding: chunked
265
265
266 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
266 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
267 $ test_archtype_deprecated zip zip tar.gz tar.bz2
267 $ test_archtype_deprecated zip zip tar.gz tar.bz2
268 % zip allowed should give 200
268 % zip allowed should give 200
269 200 Script output follows
269 200 Script output follows
270 content-disposition: attachment; filename=test-archive-1701ef1f1510.zip
270 content-disposition: attachment; filename=test-archive-1701ef1f1510.zip
271 content-type: application/zip
271 content-type: application/zip
272 date: $HTTP_DATE$
272 date: $HTTP_DATE$
273 etag: W/"*" (glob)
273 etag: W/"*" (glob)
274 server: testing stub value
274 server: testing stub value
275 transfer-encoding: chunked
275 transfer-encoding: chunked
276
276
277 body: size=(1377|1461|1489), sha1=(677b14d3d048778d5eb5552c14a67e6192068650|be6d3983aa13dfe930361b2569291cdedd02b537|1897e496871aa89ad685a92b936f5fa0d008b9e8) (re)
277 body: size=(1377|1461|1489), sha1=(677b14d3d048778d5eb5552c14a67e6192068650|be6d3983aa13dfe930361b2569291cdedd02b537|1897e496871aa89ad685a92b936f5fa0d008b9e8) (re)
278 % tar.gz and tar.bz2 disallowed should both give 403
278 % tar.gz and tar.bz2 disallowed should both give 403
279 403 Archive type not allowed: gz
279 403 Archive type not allowed: gz
280 content-type: text/html; charset=ascii
280 content-type: text/html; charset=ascii
281 date: $HTTP_DATE$
281 date: $HTTP_DATE$
282 etag: W/"*" (glob)
282 etag: W/"*" (glob)
283 server: testing stub value
283 server: testing stub value
284 transfer-encoding: chunked
284 transfer-encoding: chunked
285
285
286 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
286 body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
287 403 Archive type not allowed: bz2
287 403 Archive type not allowed: bz2
288 content-type: text/html; charset=ascii
288 content-type: text/html; charset=ascii
289 date: $HTTP_DATE$
289 date: $HTTP_DATE$
290 etag: W/"*" (glob)
290 etag: W/"*" (glob)
291 server: testing stub value
291 server: testing stub value
292 transfer-encoding: chunked
292 transfer-encoding: chunked
293
293
294 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
294 body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
295
295
296 $ echo "allow-archive = gz bz2 zip" >> .hg/hgrc
296 $ echo "allow-archive = gz bz2 zip" >> .hg/hgrc
297 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
297 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
298 $ cat hg.pid >> $DAEMON_PIDS
298 $ cat hg.pid >> $DAEMON_PIDS
299
299
300 check archive links' order
300 check archive links' order
301
301
302 $ get-with-headers.py localhost:$HGPORT "?revcount=1" | grep '/archive/tip.'
302 $ get-with-headers.py localhost:$HGPORT "?revcount=1" | grep '/archive/tip.'
303 <a href="/archive/tip.zip">zip</a>
303 <a href="/archive/tip.zip">zip</a>
304 <a href="/archive/tip.tar.gz">gz</a>
304 <a href="/archive/tip.tar.gz">gz</a>
305 <a href="/archive/tip.tar.bz2">bz2</a>
305 <a href="/archive/tip.tar.bz2">bz2</a>
306
306
307 invalid arch type should give 404
307 invalid arch type should give 404
308
308
309 $ get-with-headers.py localhost:$HGPORT "archive/tip.invalid" | head -n 1
309 $ get-with-headers.py localhost:$HGPORT "archive/tip.invalid" | head -n 1
310 404 Unsupported archive type: None
310 404 Unsupported archive type: None
311
311
312 $ TIP=`hg id -v | cut -f1 -d' '`
312 $ TIP=`hg id -v | cut -f1 -d' '`
313 $ QTIP=`hg id -q`
313 $ QTIP=`hg id -q`
314 $ cat > getarchive.py <<EOF
314 $ cat > getarchive.py <<EOF
315 > from __future__ import absolute_import
315 > from __future__ import absolute_import
316 > import os
316 > import os
317 > import sys
317 > import sys
318 > from mercurial import (
318 > from mercurial import (
319 > util,
319 > util,
320 > )
320 > )
321 > try:
321 > try:
322 > # Set stdout to binary mode for win32 platforms
322 > # Set stdout to binary mode for win32 platforms
323 > import msvcrt
323 > import msvcrt
324 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
324 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
325 > except ImportError:
325 > except ImportError:
326 > pass
326 > pass
327 > if len(sys.argv) <= 3:
327 > if len(sys.argv) <= 3:
328 > node, archive = sys.argv[1:]
328 > node, archive = sys.argv[1:]
329 > requeststr = 'cmd=archive;node=%s;type=%s' % (node, archive)
329 > requeststr = 'cmd=archive;node=%s;type=%s' % (node, archive)
330 > else:
330 > else:
331 > node, archive, file = sys.argv[1:]
331 > node, archive, file = sys.argv[1:]
332 > requeststr = 'cmd=archive;node=%s;type=%s;file=%s' % (node, archive, file)
332 > requeststr = 'cmd=archive;node=%s;type=%s;file=%s' % (node, archive, file)
333 > try:
333 > try:
334 > stdout = sys.stdout.buffer
334 > stdout = sys.stdout.buffer
335 > except AttributeError:
335 > except AttributeError:
336 > stdout = sys.stdout
336 > stdout = sys.stdout
337 > try:
337 > try:
338 > f = util.urlreq.urlopen('http://$LOCALIP:%s/?%s'
338 > f = util.urlreq.urlopen('http://$LOCALIP:%s/?%s'
339 > % (os.environ['HGPORT'], requeststr))
339 > % (os.environ['HGPORT'], requeststr))
340 > stdout.write(f.read())
340 > stdout.write(f.read())
341 > except util.urlerr.httperror as e:
341 > except util.urlerr.httperror as e:
342 > sys.stderr.write(str(e) + '\n')
342 > sys.stderr.write(str(e) + '\n')
343 > EOF
343 > EOF
344 $ "$PYTHON" getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null
344 $ "$PYTHON" getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null
345 test-archive-1701ef1f1510/.hg_archival.txt
345 test-archive-1701ef1f1510/.hg_archival.txt
346 test-archive-1701ef1f1510/.hgsub
346 test-archive-1701ef1f1510/.hgsub
347 test-archive-1701ef1f1510/.hgsubstate
347 test-archive-1701ef1f1510/.hgsubstate
348 test-archive-1701ef1f1510/bar
348 test-archive-1701ef1f1510/bar
349 test-archive-1701ef1f1510/baz/bletch
349 test-archive-1701ef1f1510/baz/bletch
350 test-archive-1701ef1f1510/foo
350 test-archive-1701ef1f1510/foo
351 test-archive-1701ef1f1510/subrepo/sub
351 test-archive-1701ef1f1510/subrepo/sub
352 $ "$PYTHON" getarchive.py "$TIP" bz2 | bunzip2 | tar tf - 2>/dev/null
352 $ "$PYTHON" getarchive.py "$TIP" bz2 | bunzip2 | tar tf - 2>/dev/null
353 test-archive-1701ef1f1510/.hg_archival.txt
353 test-archive-1701ef1f1510/.hg_archival.txt
354 test-archive-1701ef1f1510/.hgsub
354 test-archive-1701ef1f1510/.hgsub
355 test-archive-1701ef1f1510/.hgsubstate
355 test-archive-1701ef1f1510/.hgsubstate
356 test-archive-1701ef1f1510/bar
356 test-archive-1701ef1f1510/bar
357 test-archive-1701ef1f1510/baz/bletch
357 test-archive-1701ef1f1510/baz/bletch
358 test-archive-1701ef1f1510/foo
358 test-archive-1701ef1f1510/foo
359 test-archive-1701ef1f1510/subrepo/sub
359 test-archive-1701ef1f1510/subrepo/sub
360 $ "$PYTHON" getarchive.py "$TIP" zip > archive.zip
360 $ "$PYTHON" getarchive.py "$TIP" zip > archive.zip
361 $ unzip -t archive.zip
361 $ unzip -t archive.zip
362 Archive: archive.zip
362 Archive: archive.zip
363 testing: test-archive-1701ef1f1510/.hg_archival.txt*OK (glob)
363 testing: test-archive-1701ef1f1510/.hg_archival.txt*OK (glob)
364 testing: test-archive-1701ef1f1510/.hgsub*OK (glob)
364 testing: test-archive-1701ef1f1510/.hgsub*OK (glob)
365 testing: test-archive-1701ef1f1510/.hgsubstate*OK (glob)
365 testing: test-archive-1701ef1f1510/.hgsubstate*OK (glob)
366 testing: test-archive-1701ef1f1510/bar*OK (glob)
366 testing: test-archive-1701ef1f1510/bar*OK (glob)
367 testing: test-archive-1701ef1f1510/baz/bletch*OK (glob)
367 testing: test-archive-1701ef1f1510/baz/bletch*OK (glob)
368 testing: test-archive-1701ef1f1510/foo*OK (glob)
368 testing: test-archive-1701ef1f1510/foo*OK (glob)
369 testing: test-archive-1701ef1f1510/subrepo/sub*OK (glob)
369 testing: test-archive-1701ef1f1510/subrepo/sub*OK (glob)
370 No errors detected in compressed data of archive.zip.
370 No errors detected in compressed data of archive.zip.
371
371
372 test that we can download single directories and files
372 test that we can download single directories and files
373
373
374 $ "$PYTHON" getarchive.py "$TIP" gz baz | gunzip | tar tf - 2>/dev/null
374 $ "$PYTHON" getarchive.py "$TIP" gz baz | gunzip | tar tf - 2>/dev/null
375 test-archive-1701ef1f1510/baz/bletch
375 test-archive-1701ef1f1510/baz/bletch
376 $ "$PYTHON" getarchive.py "$TIP" gz foo | gunzip | tar tf - 2>/dev/null
376 $ "$PYTHON" getarchive.py "$TIP" gz foo | gunzip | tar tf - 2>/dev/null
377 test-archive-1701ef1f1510/foo
377 test-archive-1701ef1f1510/foo
378
378
379 test that we detect file patterns that match no files
379 test that we detect file patterns that match no files
380
380
381 $ "$PYTHON" getarchive.py "$TIP" gz foobar
381 $ "$PYTHON" getarchive.py "$TIP" gz foobar
382 HTTP Error 404: file(s) not found: foobar
382 HTTP Error 404: file(s) not found: foobar
383
383
384 test that we reject unsafe patterns
384 test that we reject unsafe patterns
385
385
386 $ "$PYTHON" getarchive.py "$TIP" gz relre:baz
386 $ "$PYTHON" getarchive.py "$TIP" gz relre:baz
387 HTTP Error 404: file(s) not found: relre:baz
387 HTTP Error 404: file(s) not found: relre:baz
388
388
389 $ killdaemons.py
389 $ killdaemons.py
390
390
391 $ hg archive -t tar test.tar
391 $ hg archive -t tar test.tar
392 $ tar tf test.tar
392 $ tar tf test.tar
393 test/.hg_archival.txt
393 test/.hg_archival.txt
394 test/.hgsub
394 test/.hgsub
395 test/.hgsubstate
395 test/.hgsubstate
396 test/bar
396 test/bar
397 test/baz/bletch
397 test/baz/bletch
398 test/foo
398 test/foo
399
399
400 $ hg archive --debug -t tbz2 -X baz test.tar.bz2 --config progress.debug=true
400 $ hg archive --debug -t tbz2 -X baz test.tar.bz2 --config progress.debug=true
401 archiving: 0/4 files (0.00%)
401 archiving: 0/4 files (0.00%)
402 archiving: .hgsub 1/4 files (25.00%)
402 archiving: .hgsub 1/4 files (25.00%)
403 archiving: .hgsubstate 2/4 files (50.00%)
403 archiving: .hgsubstate 2/4 files (50.00%)
404 archiving: bar 3/4 files (75.00%)
404 archiving: bar 3/4 files (75.00%)
405 archiving: foo 4/4 files (100.00%)
405 archiving: foo 4/4 files (100.00%)
406 $ bunzip2 -dc test.tar.bz2 | tar tf - 2>/dev/null
406 $ bunzip2 -dc test.tar.bz2 | tar tf - 2>/dev/null
407 test/.hg_archival.txt
407 test/.hg_archival.txt
408 test/.hgsub
408 test/.hgsub
409 test/.hgsubstate
409 test/.hgsubstate
410 test/bar
410 test/bar
411 test/foo
411 test/foo
412
412
413 $ hg archive -t tgz -p %b-%h test-%h.tar.gz
413 $ hg archive -t tgz -p %b-%h test-%h.tar.gz
414 $ gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null
414 $ gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null
415 test-1701ef1f1510/.hg_archival.txt
415 test-1701ef1f1510/.hg_archival.txt
416 test-1701ef1f1510/.hgsub
416 test-1701ef1f1510/.hgsub
417 test-1701ef1f1510/.hgsubstate
417 test-1701ef1f1510/.hgsubstate
418 test-1701ef1f1510/bar
418 test-1701ef1f1510/bar
419 test-1701ef1f1510/baz/bletch
419 test-1701ef1f1510/baz/bletch
420 test-1701ef1f1510/foo
420 test-1701ef1f1510/foo
421
421
422 $ hg archive autodetected_test.tar
422 $ hg archive autodetected_test.tar
423 $ tar tf autodetected_test.tar
423 $ tar tf autodetected_test.tar
424 autodetected_test/.hg_archival.txt
424 autodetected_test/.hg_archival.txt
425 autodetected_test/.hgsub
425 autodetected_test/.hgsub
426 autodetected_test/.hgsubstate
426 autodetected_test/.hgsubstate
427 autodetected_test/bar
427 autodetected_test/bar
428 autodetected_test/baz/bletch
428 autodetected_test/baz/bletch
429 autodetected_test/foo
429 autodetected_test/foo
430
430
431 The '-t' should override autodetection
431 The '-t' should override autodetection
432
432
433 $ hg archive -t tar autodetect_override_test.zip
433 $ hg archive -t tar autodetect_override_test.zip
434 $ tar tf autodetect_override_test.zip
434 $ tar tf autodetect_override_test.zip
435 autodetect_override_test.zip/.hg_archival.txt
435 autodetect_override_test.zip/.hg_archival.txt
436 autodetect_override_test.zip/.hgsub
436 autodetect_override_test.zip/.hgsub
437 autodetect_override_test.zip/.hgsubstate
437 autodetect_override_test.zip/.hgsubstate
438 autodetect_override_test.zip/bar
438 autodetect_override_test.zip/bar
439 autodetect_override_test.zip/baz/bletch
439 autodetect_override_test.zip/baz/bletch
440 autodetect_override_test.zip/foo
440 autodetect_override_test.zip/foo
441
441
442 $ for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
442 $ for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
443 > hg archive auto_test.$ext
443 > hg archive auto_test.$ext
444 > if [ -d auto_test.$ext ]; then
444 > if [ -d auto_test.$ext ]; then
445 > echo "extension $ext was not autodetected."
445 > echo "extension $ext was not autodetected."
446 > fi
446 > fi
447 > done
447 > done
448
448
449 $ cat > md5comp.py <<EOF
449 $ cat > md5comp.py <<EOF
450 > from __future__ import absolute_import, print_function
450 > from __future__ import absolute_import, print_function
451 > import hashlib
451 > import hashlib
452 > import sys
452 > import sys
453 > f1, f2 = sys.argv[1:3]
453 > f1, f2 = sys.argv[1:3]
454 > h1 = hashlib.md5(open(f1, 'rb').read()).hexdigest()
454 > h1 = hashlib.md5(open(f1, 'rb').read()).hexdigest()
455 > h2 = hashlib.md5(open(f2, 'rb').read()).hexdigest()
455 > h2 = hashlib.md5(open(f2, 'rb').read()).hexdigest()
456 > print(h1 == h2 or "md5 differ: " + repr((h1, h2)))
456 > print(h1 == h2 or "md5 differ: " + repr((h1, h2)))
457 > EOF
457 > EOF
458
458
459 archive name is stored in the archive, so create similar archives and
459 archive name is stored in the archive, so create similar archives and
460 rename them afterwards.
460 rename them afterwards.
461
461
462 $ hg archive -t tgz tip.tar.gz
462 $ hg archive -t tgz tip.tar.gz
463 $ mv tip.tar.gz tip1.tar.gz
463 $ mv tip.tar.gz tip1.tar.gz
464 $ sleep 1
464 $ sleep 1
465 $ hg archive -t tgz tip.tar.gz
465 $ hg archive -t tgz tip.tar.gz
466 $ mv tip.tar.gz tip2.tar.gz
466 $ mv tip.tar.gz tip2.tar.gz
467 $ "$PYTHON" md5comp.py tip1.tar.gz tip2.tar.gz
467 $ "$PYTHON" md5comp.py tip1.tar.gz tip2.tar.gz
468 True
468 True
469
469
470 $ hg archive -t zip -p /illegal test.zip
470 $ hg archive -t zip -p /illegal test.zip
471 abort: archive prefix contains illegal components
471 abort: archive prefix contains illegal components
472 [255]
472 [255]
473 $ hg archive -t zip -p very/../bad test.zip
473 $ hg archive -t zip -p very/../bad test.zip
474
474
475 $ hg archive --config ui.archivemeta=false -t zip -r 2 test.zip
475 $ hg archive --config ui.archivemeta=false -t zip -r 2 test.zip
476 $ unzip -t test.zip
476 $ unzip -t test.zip
477 Archive: test.zip
477 Archive: test.zip
478 testing: test/bar*OK (glob)
478 testing: test/bar*OK (glob)
479 testing: test/baz/bletch*OK (glob)
479 testing: test/baz/bletch*OK (glob)
480 testing: test/foo*OK (glob)
480 testing: test/foo*OK (glob)
481 No errors detected in compressed data of test.zip.
481 No errors detected in compressed data of test.zip.
482
482
483 $ hg archive -t tar - | tar tf - 2>/dev/null
483 $ hg archive -t tar - | tar tf - 2>/dev/null
484 test-1701ef1f1510/.hg_archival.txt
484 test-1701ef1f1510/.hg_archival.txt
485 test-1701ef1f1510/.hgsub
485 test-1701ef1f1510/.hgsub
486 test-1701ef1f1510/.hgsubstate
486 test-1701ef1f1510/.hgsubstate
487 test-1701ef1f1510/bar
487 test-1701ef1f1510/bar
488 test-1701ef1f1510/baz/bletch
488 test-1701ef1f1510/baz/bletch
489 test-1701ef1f1510/foo
489 test-1701ef1f1510/foo
490
490
491 $ hg archive -r 0 -t tar rev-%r.tar
491 $ hg archive -r 0 -t tar rev-%r.tar
492 $ [ -f rev-0.tar ]
492 $ [ -f rev-0.tar ]
493
493
494 test .hg_archival.txt
494 test .hg_archival.txt
495
495
496 $ hg archive ../test-tags
496 $ hg archive ../test-tags
497 $ cat ../test-tags/.hg_archival.txt
497 $ cat ../test-tags/.hg_archival.txt
498 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
498 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
499 node: 1701ef1f151069b8747038e93b5186bb43a47504
499 node: 1701ef1f151069b8747038e93b5186bb43a47504
500 branch: default
500 branch: default
501 latesttag: null
501 latesttag: null
502 latesttagdistance: 4
502 latesttagdistance: 4
503 changessincelatesttag: 4
503 changessincelatesttag: 4
504 $ hg tag -r 2 mytag
504 $ hg tag -r 2 mytag
505 $ hg tag -r 2 anothertag
505 $ hg tag -r 2 anothertag
506 $ hg archive -r 2 ../test-lasttag
506 $ hg archive -r 2 ../test-lasttag
507 $ cat ../test-lasttag/.hg_archival.txt
507 $ cat ../test-lasttag/.hg_archival.txt
508 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
508 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
509 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
509 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
510 branch: default
510 branch: default
511 tag: anothertag
511 tag: anothertag
512 tag: mytag
512 tag: mytag
513
513
514 $ hg archive -t bogus test.bogus
514 $ hg archive -t bogus test.bogus
515 abort: unknown archive type 'bogus'
515 abort: unknown archive type 'bogus'
516 [255]
516 [255]
517
517
518 enable progress extension:
518 enable progress extension:
519
519
520 $ cp $HGRCPATH $HGRCPATH.no-progress
520 $ cp $HGRCPATH $HGRCPATH.no-progress
521 $ cat >> $HGRCPATH <<EOF
521 $ cat >> $HGRCPATH <<EOF
522 > [progress]
522 > [progress]
523 > assume-tty = 1
523 > assume-tty = 1
524 > format = topic bar number
524 > format = topic bar number
525 > delay = 0
525 > delay = 0
526 > refresh = 0
526 > refresh = 0
527 > width = 60
527 > width = 60
528 > EOF
528 > EOF
529
529
530 $ hg archive ../with-progress
530 $ hg archive ../with-progress
531 \r (no-eol) (esc)
531 \r (no-eol) (esc)
532 archiving [ ] 0/6\r (no-eol) (esc)
532 archiving [ ] 0/6\r (no-eol) (esc)
533 archiving [======> ] 1/6\r (no-eol) (esc)
533 archiving [======> ] 1/6\r (no-eol) (esc)
534 archiving [=============> ] 2/6\r (no-eol) (esc)
534 archiving [=============> ] 2/6\r (no-eol) (esc)
535 archiving [====================> ] 3/6\r (no-eol) (esc)
535 archiving [====================> ] 3/6\r (no-eol) (esc)
536 archiving [===========================> ] 4/6\r (no-eol) (esc)
536 archiving [===========================> ] 4/6\r (no-eol) (esc)
537 archiving [==================================> ] 5/6\r (no-eol) (esc)
537 archiving [==================================> ] 5/6\r (no-eol) (esc)
538 archiving [==========================================>] 6/6\r (no-eol) (esc)
538 archiving [==========================================>] 6/6\r (no-eol) (esc)
539 \r (no-eol) (esc)
539 \r (no-eol) (esc)
540
540
541 cleanup after progress extension test:
541 cleanup after progress extension test:
542
542
543 $ cp $HGRCPATH.no-progress $HGRCPATH
543 $ cp $HGRCPATH.no-progress $HGRCPATH
544
544
545 server errors
545 server errors
546
546
547 $ cat errors.log
547 $ cat errors.log
548
548
549 empty repo
549 empty repo
550
550
551 $ hg init ../empty
551 $ hg init ../empty
552 $ cd ../empty
552 $ cd ../empty
553 $ hg archive ../test-empty
553 $ hg archive ../test-empty
554 abort: no working directory: please specify a revision
554 abort: no working directory: please specify a revision
555 [255]
555 [255]
556
556
557 old file -- date clamped to 1980
557 old file -- date clamped to 1980
558
558
559 $ touch -t 197501010000 old
559 $ touch -t 197501010000 old
560 $ hg add old
560 $ hg add old
561 $ hg commit -m old
561 $ hg commit -m old
562 $ hg archive ../old.zip
562 $ hg archive ../old.zip
563 $ unzip -l ../old.zip | grep -v -- ----- | egrep -v files$
563 $ unzip -l ../old.zip | grep -v -- ----- | egrep -v files$
564 Archive: ../old.zip
564 Archive: ../old.zip
565 \s*Length.* (re)
565 \s*Length.* (re)
566 *172*80*00:00*old/.hg_archival.txt (glob)
566 *172*80*00:00*old/.hg_archival.txt (glob)
567 *0*80*00:00*old/old (glob)
567 *0*80*00:00*old/old (glob)
568
568
569 test xz support only available in Python 3.4
570
571 #if py3
572 $ hg archive ../archive.txz
573 $ xz -l ../archive.txz | head -n1
574 Strms Blocks Compressed Uncompressed Ratio Check Filename
575 $ rm -f ../archive.txz
576 #else
577 $ hg archive ../archive.txz
578 abort: xz compression is only available in Python 3
579 [255]
580 #endif
581
569 show an error when a provided pattern matches no files
582 show an error when a provided pattern matches no files
570
583
571 $ hg archive -I file_that_does_not_exist.foo ../empty.zip
584 $ hg archive -I file_that_does_not_exist.foo ../empty.zip
572 abort: no files match the archive pattern
585 abort: no files match the archive pattern
573 [255]
586 [255]
574
587
575 $ hg archive -X * ../empty.zip
588 $ hg archive -X * ../empty.zip
576 abort: no files match the archive pattern
589 abort: no files match the archive pattern
577 [255]
590 [255]
578
591
579 $ cd ..
592 $ cd ..
580
593
581 issue3600: check whether "hg archive" can create archive files which
594 issue3600: check whether "hg archive" can create archive files which
582 are extracted with expected timestamp, even though TZ is not
595 are extracted with expected timestamp, even though TZ is not
583 configured as GMT.
596 configured as GMT.
584
597
585 $ mkdir issue3600
598 $ mkdir issue3600
586 $ cd issue3600
599 $ cd issue3600
587
600
588 $ hg init repo
601 $ hg init repo
589 $ echo a > repo/a
602 $ echo a > repo/a
590 $ hg -R repo add repo/a
603 $ hg -R repo add repo/a
591 $ hg -R repo commit -m '#0' -d '456789012 21600'
604 $ hg -R repo commit -m '#0' -d '456789012 21600'
592 $ cat > show_mtime.py <<EOF
605 $ cat > show_mtime.py <<EOF
593 > from __future__ import absolute_import, print_function
606 > from __future__ import absolute_import, print_function
594 > import os
607 > import os
595 > import sys
608 > import sys
596 > print(int(os.stat(sys.argv[1]).st_mtime))
609 > print(int(os.stat(sys.argv[1]).st_mtime))
597 > EOF
610 > EOF
598
611
599 $ hg -R repo archive --prefix tar-extracted archive.tar
612 $ hg -R repo archive --prefix tar-extracted archive.tar
600 $ (TZ=UTC-3; export TZ; tar xf archive.tar)
613 $ (TZ=UTC-3; export TZ; tar xf archive.tar)
601 $ "$PYTHON" show_mtime.py tar-extracted/a
614 $ "$PYTHON" show_mtime.py tar-extracted/a
602 456789012
615 456789012
603
616
604 $ hg -R repo archive --prefix zip-extracted archive.zip
617 $ hg -R repo archive --prefix zip-extracted archive.zip
605 $ (TZ=UTC-3; export TZ; unzip -q archive.zip)
618 $ (TZ=UTC-3; export TZ; unzip -q archive.zip)
606 $ "$PYTHON" show_mtime.py zip-extracted/a
619 $ "$PYTHON" show_mtime.py zip-extracted/a
607 456789012
620 456789012
608
621
609 $ cd ..
622 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now